org.apache.sentry.provider.db.service.persistent.TestSentryStore.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.sentry.provider.db.service.persistent.TestSentryStore.java

Source

/**
 * 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.sentry.provider.db.service.persistent;

import java.io.File;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import com.google.common.collect.Lists;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.security.alias.CredentialProvider;
import org.apache.hadoop.security.alias.CredentialProviderFactory;
import org.apache.hadoop.security.alias.UserProvider;
import org.apache.sentry.SentryOwnerInfo;
import org.apache.sentry.api.common.ApiConstants.PrivilegeScope;
import org.apache.sentry.api.service.thrift.TSentryPrivilegeMap;
import org.apache.sentry.core.common.exception.SentryAccessDeniedException;
import org.apache.sentry.core.common.exception.SentryInvalidInputException;
import org.apache.sentry.core.common.utils.SentryConstants;
import org.apache.sentry.core.model.db.AccessConstants;
import org.apache.sentry.core.common.exception.SentryAlreadyExistsException;
import org.apache.sentry.core.common.exception.SentryNoSuchObjectException;
import org.apache.sentry.hdfs.PathsUpdate;
import org.apache.sentry.hdfs.PermissionsUpdate;
import org.apache.sentry.hdfs.UniquePathsUpdate;
import org.apache.sentry.hdfs.Updateable;
import org.apache.sentry.hdfs.service.thrift.TPathEntry;
import org.apache.sentry.hdfs.service.thrift.TPathsDump;
import org.apache.sentry.hdfs.service.thrift.TPathsUpdate;
import org.apache.sentry.hdfs.service.thrift.TPrivilegeChanges;
import org.apache.sentry.hdfs.service.thrift.TPrivilegePrincipal;
import org.apache.sentry.hdfs.service.thrift.TPrivilegePrincipalType;
import org.apache.sentry.hdfs.service.thrift.TRoleChanges;
import org.apache.sentry.provider.db.service.model.MSentryPermChange;
import org.apache.sentry.provider.db.service.model.MSentryPathChange;
import org.apache.sentry.provider.db.service.model.MSentryPrivilege;
import org.apache.sentry.provider.db.service.model.MSentryRole;
import org.apache.sentry.provider.db.service.model.MPath;
import org.apache.sentry.api.service.thrift.TSentryActiveRoleSet;
import org.apache.sentry.api.service.thrift.TSentryAuthorizable;
import org.apache.sentry.api.service.thrift.TSentryGrantOption;
import org.apache.sentry.api.service.thrift.TSentryGroup;
import org.apache.sentry.api.service.thrift.TSentryPrivilege;
import org.apache.sentry.api.service.thrift.TSentryRole;
import org.apache.sentry.provider.db.service.model.MSentryUser;
import org.apache.sentry.provider.file.PolicyFile;
import org.apache.sentry.api.common.SentryServiceUtil;
import org.apache.sentry.service.common.ServiceConstants;
import org.apache.sentry.service.common.ServiceConstants.SentryPrincipalType;
import org.apache.sentry.service.common.ServiceConstants.ServerConfig;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.io.Files;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.apache.sentry.provider.db.service.persistent.QueryParamBuilder.newQueryParamBuilder;

import javax.jdo.JDODataStoreException;

public class TestSentryStore extends org.junit.Assert {

    private static final Logger LOGGER = LoggerFactory.getLogger(TestSentryStore.class);

    private static File dataDir;
    private static SentryStore sentryStore;
    private static String[] adminGroups = { "adminGroup1" };
    private static PolicyFile policyFile;
    private static File policyFilePath;
    final long NUM_PRIVS = 5; // > SentryStore.PrivCleaner.NOTIFY_THRESHOLD
    private static Configuration conf = null;
    private static char[] passwd = new char[] { '1', '2', '3' };

    @BeforeClass
    public static void setup() throws Exception {
        conf = new Configuration(true);
        final String ourUrl = UserProvider.SCHEME_NAME + ":///";
        conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, ourUrl);

        // enable HDFS sync, so perm and path changes will be saved into DB
        conf.set(ServiceConstants.ServerConfig.PROCESSOR_FACTORIES,
                "org.apache.sentry.hdfs.SentryHDFSServiceProcessorFactory");
        conf.set(ServiceConstants.ServerConfig.SENTRY_POLICY_STORE_PLUGINS, "org.apache.sentry.hdfs.SentryPlugin");

        // THis should be a UserGroupInformation provider
        CredentialProvider provider = CredentialProviderFactory.getProviders(conf).get(0);

        // The user credentials are stored as a static variable by UserGrouoInformation provider.
        // We need to only set the password the first time, an attempt to set it for the second
        // time fails with an exception.
        if (provider.getCredentialEntry(ServerConfig.SENTRY_STORE_JDBC_PASS) == null) {
            provider.createCredentialEntry(ServerConfig.SENTRY_STORE_JDBC_PASS, passwd);
            provider.flush();
        }

        dataDir = new File(Files.createTempDir(), "sentry_policy_db");
        conf.set(ServerConfig.SENTRY_VERIFY_SCHEM_VERSION, "false");
        conf.set(ServerConfig.SENTRY_STORE_JDBC_URL,
                "jdbc:derby:;databaseName=" + dataDir.getPath() + ";create=true");
        conf.set(ServerConfig.SENTRY_STORE_JDBC_PASS, "dummy");
        conf.setStrings(ServerConfig.ADMIN_GROUPS, adminGroups);
        conf.set(ServerConfig.SENTRY_STORE_GROUP_MAPPING, ServerConfig.SENTRY_STORE_LOCAL_GROUP_MAPPING);
        policyFilePath = new File(dataDir, "local_policy_file.ini");
        conf.set(ServerConfig.SENTRY_STORE_GROUP_MAPPING_RESOURCE, policyFilePath.getPath());

        // These tests do not need to retry transactions, so setting to 1 to reduce testing time
        conf.setInt(ServerConfig.SENTRY_STORE_TRANSACTION_RETRY, 1);

        // SentryStore should be initialized only once. The tables created by the test cases will
        // be cleaned up during the @After method.
        sentryStore = new SentryStore(conf);

        boolean hdfsSyncEnabled = SentryServiceUtil.isHDFSSyncEnabled(conf);
        sentryStore.setPersistUpdateDeltas(hdfsSyncEnabled);
    }

    @Before
    public void before() throws Exception {
        policyFile = new PolicyFile();
        String adminUser = "g1";
        addGroupsToUser(adminUser, adminGroups);
        writePolicyFile();
    }

    @After
    public void after() {
        sentryStore.clearAllTables();
    }

    @AfterClass
    public static void teardown() {
        if (dataDir != null) {
            FileUtils.deleteQuietly(dataDir);
        }

        sentryStore.stop();
    }

    /**
     * Fail test if role already exists
     * @param roleName Role name to checl
     * @throws Exception
     */
    private void checkRoleDoesNotExist(String roleName) throws Exception {
        try {
            sentryStore.getMSentryRoleByName(roleName);
            fail("Role " + roleName + "already exists");
        } catch (SentryNoSuchObjectException e) {
            // Ok
        }
    }

    /**
     * Fail test if role doesn't exist
     * @param roleName Role name to checl
     * @throws Exception
     */
    private void checkRoleExists(String roleName) throws Exception {
        assertEquals(roleName.toLowerCase(), sentryStore.getMSentryRoleByName(roleName).getRoleName());
    }

    /**
     * Create a role with the given name and verify that it is created
     * @param roleName
     * @throws Exception
     */
    private void createRole(String roleName) throws Exception {
        checkRoleDoesNotExist(roleName);
        sentryStore.createSentryRole(roleName);
        checkRoleExists(roleName);
    }

    @Test
    public void testCredentialProvider() throws Exception {
        assertArrayEquals(passwd, conf.getPassword(ServerConfig.SENTRY_STORE_JDBC_PASS));
    }

    @Test
    public void testCaseInsensitiveRole() throws Exception {
        String roleName = "newRole";
        String grantor = "g1";
        Set<TSentryGroup> groups = Sets.newHashSet();
        TSentryGroup group = new TSentryGroup();
        group.setGroupName("test-groups-g1");
        groups.add(group);

        TSentryPrivilege privilege = new TSentryPrivilege();
        privilege.setPrivilegeScope("TABLE");
        privilege.setServerName("server1");
        privilege.setDbName("default");
        privilege.setTableName("table1");
        privilege.setAction(AccessConstants.ALL);
        privilege.setCreateTime(System.currentTimeMillis());

        Set<String> users = Sets.newHashSet("user1");

        createRole(roleName);

        sentryStore.alterSentryRoleAddGroups(grantor, roleName, groups);
        sentryStore.alterSentryRoleDeleteGroups(roleName, groups);
        sentryStore.alterSentryRoleAddUsers(roleName, users);
        MSentryUser user = sentryStore.getMSentryUserByName(users.iterator().next());
        assertNotNull(user);

        sentryStore.alterSentryRoleDeleteUsers(roleName, users);
        user = sentryStore.getMSentryUserByName(users.iterator().next(), false);
        assertNull(user);

        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);
        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);
    }

    @Test
    public void testURI() throws Exception {
        String roleName1 = "test-role1";
        String roleName2 = "test-role2";
        String grantor = "g1";
        String uri1 = "file:///var/folders/dt/9zm44z9s6bjfxbrm4v36lzdc0000gp/T/1401860678102-0/data/kv1.dat";
        String uri2 = "file:///var/folders/dt/9zm44z9s6bjfxbrm4v36lzdc0000gp/T/1401860678102-0/data/kv2.dat";
        createRole(roleName1);
        createRole(roleName2);
        TSentryPrivilege tSentryPrivilege1 = new TSentryPrivilege("URI", "server1", "ALL");
        tSentryPrivilege1.setURI(uri1);
        TSentryPrivilege tSentryPrivilege2 = new TSentryPrivilege("URI", "server1", "ALL");
        tSentryPrivilege2.setURI(uri2);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(tSentryPrivilege1), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName2,
                Sets.newHashSet(tSentryPrivilege2), null);

        TSentryAuthorizable tSentryAuthorizable1 = new TSentryAuthorizable();
        tSentryAuthorizable1.setUri(uri1);
        tSentryAuthorizable1.setServer("server1");

        TSentryAuthorizable tSentryAuthorizable2 = new TSentryAuthorizable();
        tSentryAuthorizable2.setUri(uri2);
        tSentryAuthorizable2.setServer("server1");

        Set<TSentryPrivilege> privileges = sentryStore.getTSentryPrivileges(SentryPrincipalType.ROLE,
                new HashSet<String>(Arrays.asList(roleName1, roleName2)), tSentryAuthorizable1);

        assertTrue(privileges.size() == 1);

        //Test with other URI Authorizable
        privileges = sentryStore.getTSentryPrivileges(SentryPrincipalType.ROLE,
                new HashSet<String>(Arrays.asList(roleName1, roleName2)), tSentryAuthorizable2);
        assertTrue(privileges.size() == 1);

        Set<TSentryGroup> tSentryGroups = new HashSet<TSentryGroup>();
        tSentryGroups.add(new TSentryGroup("group1"));
        sentryStore.alterSentryRoleAddGroups(grantor, roleName1, tSentryGroups);
        sentryStore.alterSentryRoleAddUsers(roleName1, Sets.newHashSet("user1"));
        sentryStore.alterSentryRoleAddGroups(grantor, roleName2, tSentryGroups);
        sentryStore.alterSentryRoleAddUsers(roleName2, Sets.newHashSet("user1"));

        TSentryActiveRoleSet thriftRoleSet = new TSentryActiveRoleSet(true,
                new HashSet<String>(Arrays.asList(roleName1, roleName2)));

        // list privilege for group only
        Set<String> privs = sentryStore.listSentryPrivilegesForProvider(
                new HashSet<String>(Arrays.asList("group1")), Sets.newHashSet(""), thriftRoleSet,
                tSentryAuthorizable1);
        assertTrue(privs.size() == 1);
        assertTrue(privs.contains("server=server1->uri=" + uri1 + "->action=all"));

        privs = sentryStore.listSentryPrivilegesForProvider(new HashSet<String>(Arrays.asList("group1")),
                Sets.newHashSet(""), thriftRoleSet, tSentryAuthorizable2);
        assertTrue(privs.size() == 1);
        assertTrue(privs.contains("server=server1->uri=" + uri2 + "->action=all"));

        // list privilege for user only
        privs = sentryStore.listSentryPrivilegesForProvider(new HashSet<String>(Arrays.asList("")),
                Sets.newHashSet("user1"), thriftRoleSet, tSentryAuthorizable1);
        assertTrue(privs.size() == 1);
        assertTrue(privs.contains("server=server1->uri=" + uri1 + "->action=all"));

        privs = sentryStore.listSentryPrivilegesForProvider(new HashSet<String>(Arrays.asList("")),
                Sets.newHashSet("user1"), thriftRoleSet, tSentryAuthorizable2);
        assertTrue(privs.size() == 1);
        assertTrue(privs.contains("server=server1->uri=" + uri2 + "->action=all"));

        // list privilege for both user and group
        privs = sentryStore.listSentryPrivilegesForProvider(new HashSet<String>(Arrays.asList("group1")),
                Sets.newHashSet("user1"), thriftRoleSet, tSentryAuthorizable1);
        assertTrue(privs.size() == 1);
        assertTrue(privs.contains("server=server1->uri=" + uri1 + "->action=all"));

        privs = sentryStore.listSentryPrivilegesForProvider(new HashSet<String>(Arrays.asList("group1")),
                Sets.newHashSet("user1"), thriftRoleSet, tSentryAuthorizable2);
        assertTrue(privs.size() == 1);
        assertTrue(privs.contains("server=server1->uri=" + uri2 + "->action=all"));
    }

    @Test
    public void testURIGrantRevokeOnEmptyPath() throws Exception {
        String roleName = "test-empty-uri-role";
        String uri = "";
        createRole(roleName);
        TSentryPrivilege tSentryPrivilege = new TSentryPrivilege("URI", "server1", "ALL");
        tSentryPrivilege.setURI(uri);
        //Test grant on empty URI
        try {
            sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName,
                    Sets.newHashSet(tSentryPrivilege), null);
            fail("Expected SentryInvalidInputException");
        } catch (SentryInvalidInputException e) {
            // expected
        }
        //Test revoke on empty URI
        try {
            sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName,
                    Sets.newHashSet(tSentryPrivilege), null);
            fail("Expected SentryInvalidInputException");
        } catch (SentryInvalidInputException e) {
            // expected
        }
    }

    @Test
    public void testCreateDuplicateRole() throws Exception {
        String roleName = "test-dup-role";
        createRole(roleName);
        try {
            sentryStore.createSentryRole(roleName);
            fail("Expected SentryAlreadyExistsException");
        } catch (SentryAlreadyExistsException e) {
            // expected
        }
    }

    @Test
    public void testCaseSensitiveScope() throws Exception {
        String roleName = "role1";
        createRole(roleName);
        TSentryPrivilege sentryPrivilege = new TSentryPrivilege("Database", "server1", "all");
        sentryPrivilege.setDbName("db1");
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(sentryPrivilege),
                null);
    }

    /**
     * Create a new role and then destroy it
     * @throws Exception
     */
    @Test
    public void testCreateDropRole() throws Exception {
        String roleName = "test-drop-role";
        createRole(roleName);
        sentryStore.dropSentryRole(roleName);
        checkRoleDoesNotExist(roleName);
    }

    @Test
    public void testAddDeleteGroupsNonExistantRole() throws Exception {
        String roleName = "non-existant-role";
        String grantor = "g1";
        Set<TSentryGroup> groups = Sets.newHashSet();
        Set<String> users = Sets.newHashSet(grantor);
        try {
            sentryStore.alterSentryRoleAddGroups(grantor, roleName, groups);
            fail("Expected SentryNoSuchObjectException exception");
        } catch (SentryNoSuchObjectException e) {
            // excepted exception
        }
        try {
            sentryStore.alterSentryRoleAddUsers(roleName, users);
            fail("Expected SentryNoSuchObjectException exception");
        } catch (SentryNoSuchObjectException e) {
            // excepted exception
        }
    }

    @Test
    public void testAddDeleteGroups() throws Exception {
        String roleName = "test-groups";
        String grantor = "g1";
        createRole(roleName);
        Set<TSentryGroup> groups = Sets.newHashSet();
        TSentryGroup group = new TSentryGroup();
        group.setGroupName("test-groups-g1");
        groups.add(group);
        group = new TSentryGroup();
        group.setGroupName("test-groups-g2");
        groups.add(group);
        sentryStore.alterSentryRoleAddGroups(grantor, roleName, groups);
        sentryStore.alterSentryRoleDeleteGroups(roleName, groups);
        MSentryRole role = sentryStore.getMSentryRoleByName(roleName);
        assertEquals(Collections.emptySet(), role.getGroups());
    }

    @Test
    public void testAddDeleteUsers() throws Exception {
        String roleName = "test-users";
        createRole(roleName);
        Set<String> users = Sets.newHashSet("test-user-u1", "test-user-u2");
        sentryStore.alterSentryRoleAddUsers(roleName, users);
        MSentryRole role = sentryStore.getMSentryRoleByName(roleName);
        role.getUsers().size();
        sentryStore.alterSentryRoleDeleteUsers(roleName, users);
        role = sentryStore.getMSentryRoleByName(roleName);
        assertEquals(0, role.getUsers().size());
    }

    @Test
    public void testGetTSentryRolesForUser() throws Exception {
        // Test the method GetTSentryRolesForUser according to the following test data:
        // user1->group1
        // user2->group1
        // user3->group1, group2
        // user4->group2, group3
        // group1->r1
        // group2->r2
        // group3->r2
        // user2->r3
        // user4->r3
        String roleName1 = "r1";
        String roleName2 = "r2";
        String roleName3 = "r3";
        String user1 = "u1";
        String user2 = "u2";
        String user3 = "u3";
        String user4 = "u4";
        String group1 = "group1";
        String group2 = "group2";
        String group3 = "group3";
        Map<String, Set<String>> userToGroups = Maps.newHashMap();
        userToGroups.put(user1, Sets.newHashSet(group1));
        userToGroups.put(user2, Sets.newHashSet(group1));
        userToGroups.put(user3, Sets.newHashSet(group1, group2));
        userToGroups.put(user4, Sets.newHashSet(group2, group3));

        sentryStore.createSentryRole(roleName1);
        sentryStore.createSentryRole(roleName2);
        sentryStore.createSentryRole(roleName3);
        sentryStore.alterSentryRoleAddUsers(roleName1, Sets.newHashSet(user1));
        sentryStore.alterSentryRoleAddUsers(roleName2, Sets.newHashSet(user2));
        sentryStore.alterSentryRoleAddUsers(roleName2, Sets.newHashSet(user3));
        sentryStore.alterSentryRoleAddUsers(roleName3, Sets.newHashSet(user2, user4));

        Set<TSentryRole> roles = sentryStore.getTSentryRolesByUserNames(Sets.newHashSet(user1));
        assertEquals(1, roles.size());
        for (TSentryRole role : roles) {
            assertTrue(roleName1.equals(role.getRoleName()));
        }

        roles = sentryStore.getTSentryRolesByUserNames(Sets.newHashSet(user2));
        assertEquals(2, roles.size());
        for (TSentryRole role : roles) {
            assertTrue(roleName2.equals(role.getRoleName()) || roleName3.equals(role.getRoleName()));
        }

        roles = sentryStore.getTSentryRolesByUserNames(Sets.newHashSet(user3));
        assertEquals(1, roles.size());
        for (TSentryRole role : roles) {
            assertTrue(roleName2.equals(role.getRoleName()));
        }

        roles = sentryStore.getTSentryRolesByUserNames(Sets.newHashSet(user4));
        assertEquals(1, roles.size());
        for (TSentryRole role : roles) {
            assertTrue(roleName3.equals(role.getRoleName()));
        }
    }

    @Test
    public void testGetTSentryRolesForUsers() throws Exception {
        // Test the method getTSentryRolesByUserNames according to the following test data:
        // user1->r1
        // user2->r3
        // user3->r2
        // user4->r3, r2
        String roleName1 = "r1";
        String roleName2 = "r2";
        String roleName3 = "r3";
        String user1 = "u1";
        String user2 = "u2";
        String user3 = "u3";
        String user4 = "u4";

        createRole(roleName1);
        createRole(roleName2);
        createRole(roleName3);
        sentryStore.alterSentryRoleAddUsers(roleName1, Sets.newHashSet(user1));
        sentryStore.alterSentryRoleAddUsers(roleName2, Sets.newHashSet(user3));
        sentryStore.alterSentryRoleAddUsers(roleName2, Sets.newHashSet(user4));
        sentryStore.alterSentryRoleAddUsers(roleName3, Sets.newHashSet(user2, user4));

        Set<String> userSet1 = Sets.newHashSet(user1, user2, user3);
        Set<String> roleSet1 = Sets.newHashSet(roleName1, roleName2, roleName3);

        Set<String> userSet2 = Sets.newHashSet(user4);
        Set<String> roleSet2 = Sets.newHashSet(roleName2, roleName3);

        Set<String> userSet3 = Sets.newHashSet("foo");
        Set<String> roleSet3 = Sets.newHashSet();

        // Query for multiple users
        Set<String> roles = convertToRoleNameSet(sentryStore.getTSentryRolesByUserNames(userSet1));
        assertEquals("Returned roles should match the expected roles", 0,
                Sets.symmetricDifference(roles, roleSet1).size());

        // Query for single users
        roles = convertToRoleNameSet(sentryStore.getTSentryRolesByUserNames(userSet2));
        assertEquals("Returned roles should match the expected roles", 0,
                Sets.symmetricDifference(roles, roleSet2).size());

        // Query for non-existing user
        roles = convertToRoleNameSet(sentryStore.getTSentryRolesByUserNames(userSet3));
        assertEquals("Returned roles should match the expected roles", 0,
                Sets.symmetricDifference(roles, roleSet3).size());
    }

    private Set<String> convertToRoleNameSet(Set<TSentryRole> tSentryRoles) {
        Set<String> roleNameSet = Sets.newHashSet();
        for (TSentryRole role : tSentryRoles) {
            roleNameSet.add(role.getRoleName());
        }
        return roleNameSet;
    }

    @Test
    public void testGetTSentryRolesForGroups() throws Exception {
        // Test the method getRoleNamesForGroups according to the following test data:
        // group1->r1
        // group2->r2
        // group3->r2
        String grantor = "g1";
        String roleName1 = "r1";
        String roleName2 = "r2";
        String roleName3 = "r3";
        String group1 = "group1";
        String group2 = "group2";
        String group3 = "group3";

        createRole(roleName1);
        createRole(roleName2);
        createRole(roleName3);
        sentryStore.alterSentryRoleAddGroups(grantor, roleName1, Sets.newHashSet(new TSentryGroup(group1)));
        sentryStore.alterSentryRoleAddGroups(grantor, roleName2,
                Sets.newHashSet(new TSentryGroup(group2), new TSentryGroup(group3)));

        Set<String> groupSet1 = Sets.newHashSet(group1, group2, group3);
        Set<String> roleSet1 = Sets.newHashSet(roleName1, roleName2);

        Set<String> groupSet2 = Sets.newHashSet(group1);
        Set<String> roleSet2 = Sets.newHashSet(roleName1);

        Set<String> groupSet3 = Sets.newHashSet("foo");
        Set<String> roleSet3 = Sets.newHashSet();

        // Query for multiple groups
        Set<String> roles = sentryStore.getRoleNamesForGroups(groupSet1);
        assertEquals("Returned roles should match the expected roles", 0,
                Sets.symmetricDifference(roles, roleSet1).size());

        // Query for single group
        roles = sentryStore.getRoleNamesForGroups(groupSet2);
        assertEquals("Returned roles should match the expected roles", 0,
                Sets.symmetricDifference(roles, roleSet2).size());

        // Query for non-existing group
        roles = sentryStore.getRoleNamesForGroups(groupSet3);
        assertEquals("Returned roles should match the expected roles", 0,
                Sets.symmetricDifference(roles, roleSet3).size());
    }

    @Test
    public void testGrantRevokePrivilege() throws Exception {
        String roleName = "test-privilege";
        String server = "server1";
        String db = "db1";
        String table = "tbl1";
        createRole(roleName);
        TSentryPrivilege privilege = new TSentryPrivilege();
        privilege.setPrivilegeScope("TABLE");
        privilege.setServerName(server);
        privilege.setDbName(db);
        privilege.setTableName(table);
        privilege.setAction(AccessConstants.ALL);
        privilege.setCreateTime(System.currentTimeMillis());
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);
        MSentryRole role = sentryStore.getMSentryRoleByName(roleName);
        Set<MSentryPrivilege> privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 1, privileges.size());
        privilege.setAction(AccessConstants.SELECT);
        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);
        // after having ALL and revoking SELECT, we should have INSERT
        role = sentryStore.getMSentryRoleByName(roleName);
        privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 1, privileges.size());
        for (MSentryPrivilege mPrivilege : privileges) {
            assertEquals(server, mPrivilege.getServerName());
            assertEquals(db, mPrivilege.getDbName());
            assertEquals(table, mPrivilege.getTableName());
            assertNotSame(AccessConstants.SELECT, mPrivilege.getAction());
            assertFalse(mPrivilege.getGrantOption());
        }
        long numDBPrivs = sentryStore.countMSentryPrivileges();
        assertEquals("Privilege count", numDBPrivs, 1);
    }

    private void verifyOrphanCleanup() throws Exception {
        assertFalse("Failed to cleanup orphaned privileges", sentryStore.findOrphanedPrivileges());
    }

    /**
     * Create several privileges in the database, then delete the role that
     * created them.  This makes them all orphans.  Wait a bit to ensure the
     * cleanup thread runs, and expect them all to be gone from the database.
     * @throws Exception
     */
    // @Ignore("Disabled with SENTRY-545 following SENTRY-140 problems")
    @Test
    public void testPrivilegeCleanup() throws Exception {
        final String roleName = "test-priv-cleanup";
        final String server = "server";
        final String dBase = "db";
        final String table = "table-";

        createRole(roleName);

        // Create NUM_PRIVS unique privilege objects in the database
        for (int i = 0; i < NUM_PRIVS; i++) {
            TSentryPrivilege priv = new TSentryPrivilege();
            priv.setPrivilegeScope("TABLE");
            priv.setServerName(server);
            priv.setAction(AccessConstants.ALL);
            priv.setCreateTime(System.currentTimeMillis());
            priv.setTableName(table + i);
            priv.setDbName(dBase);
            sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(priv), null);
        }

        // Make sure we really have the expected number of privs in the database
        assertEquals(sentryStore.countMSentryPrivileges(), NUM_PRIVS);

        // Now to make a bunch of orphans, we just remove the role that
        // created them.
        sentryStore.dropSentryRole(roleName);

        // Now wait and see if the orphans get cleaned up
        verifyOrphanCleanup();

        List<MSentryPrivilege> list = sentryStore.getAllMSentryPrivileges();
        assertEquals(list.size(), 0);
    }

    /**
     * Much like testPrivilegeCleanup, make a lot of privileges and make sure
     * they get cleaned up.  The difference here is that the privileges are
     * created by granting ALL and then removing SELECT - thus leaving INSERT.
     * This test exists because the revocation plays havoc with the orphan
     * cleanup thread.
     * @throws Exception
     */
    // @Ignore("Disabled with SENTRY-545 following SENTRY-140 problems")
    @Test
    public void testPrivilegeCleanup2() throws Exception {
        final String roleName = "test-priv-cleanup";
        final String server = "server";
        final String dBase = "db";
        final String table = "table-";

        createRole(roleName);

        // Create NUM_PRIVS unique privilege objects in the database once more,
        // this time granting ALL and revoking SELECT to make INSERT.
        for (int i = 0; i < NUM_PRIVS; i++) {
            TSentryPrivilege priv = new TSentryPrivilege();
            priv.setPrivilegeScope("DATABASE");
            priv.setServerName(server);
            priv.setAction(AccessConstants.ALL);
            priv.setCreateTime(System.currentTimeMillis());
            priv.setTableName(table + i);
            priv.setDbName(dBase);
            priv.setGrantOption(TSentryGrantOption.TRUE);
            sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(priv), null);

            priv.setAction(AccessConstants.SELECT);
            priv.setGrantOption(TSentryGrantOption.UNSET);
            sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(priv),
                    null);
            // after having ALL and revoking SELECT, we should have INSERT
            MSentryRole role = sentryStore.getMSentryRoleByName(roleName);
            Set<MSentryPrivilege> privileges = role.getPrivileges();
            assertEquals(privileges.toString(), 1 * (i + 1), privileges.size());
            for (MSentryPrivilege mSentryPrivilege : privileges) {
                assertNotSame(AccessConstants.INSERT, mSentryPrivilege.getAction());
                assertNotSame(AccessConstants.ALL, mSentryPrivilege.getAction());
            }
        }

        // Drop the role and clean up as before
        sentryStore.dropSentryRole(roleName);
        verifyOrphanCleanup();
        //There should not be any Privileges left
        List<MSentryPrivilege> list = sentryStore.getAllMSentryPrivileges();
        assertEquals(list.size(), 0);
    }

    /**
     * This method tries to add ALL privileges on
     * databases to a role and immediately revokes
     * SELECT and INSERT privileges. At the end of
     * each iteration we should not find any privileges
     * for that role. Finally we should not find any
     * privileges, as we are cleaning up orphan privileges
     * immediately.
     * @throws Exception
     */
    @Test
    public void testPrivilegeCleanup3() throws Exception {
        final String roleName = "test-priv-cleanup";
        final String server = "server";
        final String dBase = "db";
        final String table = "table-";

        createRole(roleName);

        // Create NUM_PRIVS unique privilege objects in the database once more,
        // this time granting ALL and revoking SELECT to make INSERT.
        for (int i = 0; i < NUM_PRIVS; i++) {
            TSentryPrivilege priv = new TSentryPrivilege();
            priv.setPrivilegeScope("DATABASE");
            priv.setServerName(server);
            priv.setAction(AccessConstants.ALL);
            priv.setCreateTime(System.currentTimeMillis());
            priv.setTableName(table + i);
            priv.setDbName(dBase);
            priv.setGrantOption(TSentryGrantOption.TRUE);
            sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(priv), null);

            priv.setAction(AccessConstants.SELECT);
            priv.setGrantOption(TSentryGrantOption.UNSET);
            sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(priv),
                    null);

            assertFalse(sentryStore.findOrphanedPrivileges());

            //After having ALL and revoking SELECT, we should have INSERT
            //Remove the INSERT privilege as well.
            //There should not be any more privileges in the sentry store
            priv.setAction(AccessConstants.INSERT);
            sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(priv),
                    null);

            MSentryRole role = sentryStore.getMSentryRoleByName(roleName);
            assertEquals("Privilege Count", 0, role.getPrivileges().size());

            priv.setAction(AccessConstants.CREATE);
            sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(priv),
                    null);

            role = sentryStore.getMSentryRoleByName(roleName);
            assertEquals("Privilege Count", 0, role.getPrivileges().size());

            priv.setAction(AccessConstants.DROP);
            sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(priv),
                    null);

            role = sentryStore.getMSentryRoleByName(roleName);
            assertEquals("Privilege Count", 0, role.getPrivileges().size());

            priv.setAction(AccessConstants.ALTER);
            sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(priv),
                    null);

            role = sentryStore.getMSentryRoleByName(roleName);
            assertEquals("Privilege Count", 0, role.getPrivileges().size());
        }

        // Drop the role and clean up as before
        verifyOrphanCleanup();

        //There should not be any Privileges left
        List<MSentryPrivilege> list = sentryStore.getAllMSentryPrivileges();
        assertEquals(list.size(), 0);

    }

    /**
     * This method "n" privileges with action "ALL" on
     * tables to a role. Later we are revoking insert
     * and select privileges to of the tables making the
     * the privilege orphan. Finally we should find only
     * n -1 privileges, as we are cleaning up orphan
     * privileges immediately.
     * @throws Exception
     */
    @Test
    public void testPrivilegeCleanup4() throws Exception {
        final String roleName = "test-priv-cleanup";
        final String server = "server";
        final String dBase = "db";
        final String table = "table-";

        createRole(roleName);

        // Create NUM_PRIVS unique privilege objects in the database
        for (int i = 0; i < NUM_PRIVS; i++) {
            TSentryPrivilege priv = new TSentryPrivilege();
            priv.setPrivilegeScope("TABLE");
            priv.setServerName(server);
            priv.setAction(AccessConstants.ALL);
            priv.setCreateTime(System.currentTimeMillis());
            priv.setTableName(table + i);
            priv.setDbName(dBase);
            sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(priv), null);
        }

        // Make sure we really have the expected number of privs in the database
        assertEquals(sentryStore.countMSentryPrivileges(), NUM_PRIVS);

        //Revoking INSERT privilege. This is change the privilege to SELECT, CREATE, DROP, ALTER
        TSentryPrivilege priv = new TSentryPrivilege();
        priv.setPrivilegeScope("TABLE");
        priv.setServerName(server);
        priv.setAction(AccessConstants.INSERT);
        priv.setCreateTime(System.currentTimeMillis());
        priv.setTableName(table + '0');
        priv.setDbName(dBase);
        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(priv), null);

        //There should be SELECT privilege in the sentry store
        priv = new TSentryPrivilege();
        priv.setPrivilegeScope("TABLE");
        priv.setServerName(server);
        priv.setAction(AccessConstants.SELECT);
        priv.setCreateTime(System.currentTimeMillis());
        priv.setTableName(table + '0');
        priv.setDbName(dBase);
        MSentryPrivilege mPriv = sentryStore.findMSentryPrivilegeFromTSentryPrivilege(priv);
        assertNotNull(mPriv);

        MSentryRole role = sentryStore.getMSentryRoleByName(roleName);

        // should have NUM_PRIVS - 1 ALL privileges, and 4 privileges (SELECT, CREATE, DROP, ALTER)
        assertEquals("Privilege Count", NUM_PRIVS, role.getPrivileges().size());

        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(priv), null);
        role = sentryStore.getMSentryRoleByName(roleName);
        assertEquals("Privilege Count", NUM_PRIVS - 1, role.getPrivileges().size());

    }

    /**
     * This method tries to add alter privileges on
     * databases to a role and immediately revokes
     * them. At the end of each iteration we should
     * not find any privileges for that role
     * Finally we should not find any privileges, as
     * we are cleaning up orphan privileges immediately.
     * @throws Exception
     */
    @Test
    public void testPrivilegeCleanup5() throws Exception {
        final String roleName = "test-priv-cleanup";
        final String server = "server";
        final String dBase = "db";
        final String table = "table-";

        createRole(roleName);

        // Create NUM_PRIVS unique privilege objects in the database once more,
        // this time granting ALL and revoking SELECT to make INSERT.
        for (int i = 0; i < NUM_PRIVS; i++) {
            TSentryPrivilege priv = new TSentryPrivilege();
            priv.setPrivilegeScope("DATABASE");
            priv.setServerName(server);
            priv.setAction(AccessConstants.ALTER);
            priv.setCreateTime(System.currentTimeMillis());
            priv.setTableName(table + i);
            priv.setDbName(dBase);
            priv.setGrantOption(TSentryGrantOption.TRUE);
            sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(priv), null);

            priv.setAction(AccessConstants.ALTER);
            sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(priv),
                    null);

            MSentryRole role = sentryStore.getMSentryRoleByName(roleName);
            assertEquals("Privilege Count", 0, role.getPrivileges().size());
        }
        //There should not be any Privileges left
        List<MSentryPrivilege> list = sentryStore.getAllMSentryPrivileges();
        assertEquals(list.size(), 0);

    }

    @Test
    public void testDropIndividualPrivilegesWhenGrantAllIsGranted() throws Exception {
        final String ROLE_NAME = "r1";
        final String SERVER_NAME = "server1";
        final String DB_NAME = "db1";
        final String TABLE_NAME = "table1";

        TSentryPrivilege allPrivilege = toTSentryPrivilege(AccessConstants.ACTION_ALL,
                PrivilegeScope.SERVER.toString(), SERVER_NAME, DB_NAME, TABLE_NAME, TSentryGrantOption.FALSE);

        TSentryPrivilege allWithGrant = toTSentryPrivilege(AccessConstants.ACTION_ALL,
                PrivilegeScope.SERVER.toString(), SERVER_NAME, DB_NAME, TABLE_NAME, TSentryGrantOption.TRUE);

        TSentryPrivilege ownerPrivilege = toTSentryPrivilege(AccessConstants.OWNER,
                PrivilegeScope.DATABASE.toString(), SERVER_NAME, DB_NAME, TABLE_NAME);

        Set<TSentryPrivilege> grantPrivileges = Sets.newHashSet(
                toTSentryPrivilege(AccessConstants.SELECT, PrivilegeScope.SERVER.toString(), SERVER_NAME, DB_NAME,
                        TABLE_NAME),
                toTSentryPrivilege(AccessConstants.INSERT, PrivilegeScope.SERVER.toString(), SERVER_NAME, DB_NAME,
                        TABLE_NAME),
                toTSentryPrivilege(AccessConstants.CREATE, PrivilegeScope.SERVER.toString(), SERVER_NAME, DB_NAME,
                        TABLE_NAME),
                toTSentryPrivilege(AccessConstants.ALTER, PrivilegeScope.SERVER.toString(), SERVER_NAME, DB_NAME,
                        TABLE_NAME),
                toTSentryPrivilege(AccessConstants.DROP, PrivilegeScope.SERVER.toString(), SERVER_NAME, DB_NAME,
                        TABLE_NAME),
                toTSentryPrivilege(AccessConstants.INDEX, PrivilegeScope.SERVER.toString(), SERVER_NAME, DB_NAME,
                        TABLE_NAME),
                toTSentryPrivilege(AccessConstants.LOCK, PrivilegeScope.SERVER.toString(), SERVER_NAME, DB_NAME,
                        TABLE_NAME),

                // This special privilege will not be removed when granting all privileges
                ownerPrivilege);

        // Grant individual privileges to a role
        createRole(ROLE_NAME);
        sentryStore.alterSentryRoleGrantPrivileges(ROLE_NAME, grantPrivileges);

        // Check those individual privileges are granted
        Set<TSentryPrivilege> rolePrivileges = sentryStore.getAllTSentryPrivilegesByRoleName(ROLE_NAME);
        assertEquals(grantPrivileges.size(), rolePrivileges.size());
        for (TSentryPrivilege privilege : grantPrivileges) {
            assertTrue(String.format("Privilege %s was not granted.", privilege.getAction()),
                    rolePrivileges.contains(privilege));
        }

        // Grant the ALL privilege (this should remove all individual privileges, and grant only ALL)
        sentryStore.alterSentryRoleGrantPrivileges(ROLE_NAME, Sets.newHashSet(allPrivilege));

        // Check the ALL and OWNER privileges are the only privileges
        rolePrivileges = sentryStore.getAllTSentryPrivilegesByRoleName(ROLE_NAME);
        assertEquals(2, rolePrivileges.size()); // ALL and OWNER privileges should be there
        assertTrue("Privilege ALL was not granted.", rolePrivileges.contains(allPrivilege));
        assertTrue("Privilege OWNER was dropped.", rolePrivileges.contains(ownerPrivilege));

        // Check the ALL WITH GRANT privilege just replaces the ALL and keeps the OWNER privilege
        sentryStore.alterSentryRoleGrantPrivileges(ROLE_NAME, Sets.newHashSet(allWithGrant));
        rolePrivileges = sentryStore.getAllTSentryPrivilegesByRoleName(ROLE_NAME);
        assertTrue("Privilege ALL WITH GRANT was not granted.", rolePrivileges.contains(allWithGrant));
        assertTrue("Privilege OWNER was dropped.", rolePrivileges.contains(ownerPrivilege));

        // Check the ALL privilege just replaces the ALL WITH GRANT and keeps the OWNER privilege
        sentryStore.alterSentryRoleGrantPrivileges(ROLE_NAME, Sets.newHashSet(allPrivilege));
        rolePrivileges = sentryStore.getAllTSentryPrivilegesByRoleName(ROLE_NAME);
        assertTrue("Privilege ALL was not granted.", rolePrivileges.contains(allPrivilege));
        assertTrue("Privilege OWNER was dropped.", rolePrivileges.contains(ownerPrivilege));

        // Clean-up test
        sentryStore.dropSentryRole(ROLE_NAME);
    }

    //TODO Use new transaction Manager logic, Instead of

    @Test
    public void testGrantRevokeMultiPrivileges() throws Exception {
        String roleName = "test-privilege";
        String server = "server1";
        String db = "db1";
        String table = "tbl1";
        String[] columns = { "c1", "c2", "c3", "c4" };
        createRole(roleName);
        Set<TSentryPrivilege> tPrivileges = Sets.newHashSet();
        for (String column : columns) {
            TSentryPrivilege privilege = new TSentryPrivilege();
            privilege.setPrivilegeScope("Column");
            privilege.setServerName(server);
            privilege.setDbName(db);
            privilege.setTableName(table);
            privilege.setColumnName(column);
            privilege.setAction(AccessConstants.SELECT);
            privilege.setCreateTime(System.currentTimeMillis());
            tPrivileges.add(privilege);
        }
        sentryStore.alterSentryRoleGrantPrivileges(roleName, tPrivileges);
        MSentryRole role = sentryStore.getMSentryRoleByName(roleName);
        Set<MSentryPrivilege> privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 4, privileges.size());

        tPrivileges = Sets.newHashSet();
        for (int i = 0; i < 2; i++) {
            TSentryPrivilege privilege = new TSentryPrivilege();
            privilege.setPrivilegeScope("Column");
            privilege.setServerName(server);
            privilege.setDbName(db);
            privilege.setTableName(table);
            privilege.setColumnName(columns[i]);
            privilege.setAction(AccessConstants.SELECT);
            privilege.setCreateTime(System.currentTimeMillis());
            tPrivileges.add(privilege);
        }
        sentryStore.alterSentryRoleRevokePrivileges(roleName, tPrivileges);
        role = sentryStore.getMSentryRoleByName(roleName);
        privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 2, privileges.size());

        TSentryPrivilege privilege = new TSentryPrivilege();
        privilege.setPrivilegeScope("Table");
        privilege.setServerName(server);
        privilege.setDbName(db);
        privilege.setTableName(table);
        privilege.setAction(AccessConstants.SELECT);
        privilege.setCreateTime(System.currentTimeMillis());
        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);
        // After revoking table scope, we will have 0 privileges
        role = sentryStore.getMSentryRoleByName(roleName);
        privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 0, privileges.size());
    }

    /**
     * Regression test for SENTRY-74 and SENTRY-552
     */
    @Test
    public void testGrantRevokePrivilegeWithColumn() throws Exception {
        String roleName = "test-col-privilege";
        String server = "server1";
        String db = "db1";
        String table = "tbl1";
        String column1 = "c1";
        String column2 = "c2";
        createRole(roleName);
        TSentryPrivilege privilege = new TSentryPrivilege();
        privilege.setPrivilegeScope("COLUMN");
        privilege.setServerName(server);
        privilege.setDbName(db);
        privilege.setTableName(table);
        privilege.setColumnName(column1);
        privilege.setAction(AccessConstants.ALL);
        privilege.setCreateTime(System.currentTimeMillis());

        // Grant ALL on c1 and c2
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);
        privilege.setColumnName(column2);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);
        MSentryRole role = sentryStore.getMSentryRoleByName(roleName);
        Set<MSentryPrivilege> privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 2, privileges.size());

        // Revoke SELECT on c2
        privilege.setAction(AccessConstants.SELECT);
        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);

        // At this point c1 has ALL privileges and c2 should have (INSERT, CREATE, DROP, ALTER)
        // after revoking SELECT
        role = sentryStore.getMSentryRoleByName(roleName);
        privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 2, privileges.size());
        for (MSentryPrivilege mPrivilege : privileges) {
            assertEquals(server, mPrivilege.getServerName());
            assertEquals(db, mPrivilege.getDbName());
            assertEquals(table, mPrivilege.getTableName());
            assertFalse(mPrivilege.getGrantOption());
            if (mPrivilege.getColumnName().equals(column1)) {
                assertEquals(AccessConstants.ALL, mPrivilege.getAction());
            } else if (mPrivilege.getColumnName().equals(column2)) {
                assertNotSame(AccessConstants.SELECT, mPrivilege.getAction());
                assertNotSame(AccessConstants.ALL, mPrivilege.getAction());
            } else {
                fail("Unexpected column name: " + mPrivilege.getColumnName());
            }
        }

        // after revoking INSERT table level privilege will remove INSERT privileges from column2
        // and downgrade column1 to (SELECT) privileges.
        privilege = new TSentryPrivilege();
        privilege.setPrivilegeScope("TABLE");
        privilege.setServerName(server);
        privilege.setDbName(db);
        privilege.setTableName(table);
        privilege.setAction(AccessConstants.INSERT);
        privilege.setCreateTime(System.currentTimeMillis());
        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);
        role = sentryStore.getMSentryRoleByName(roleName);
        privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 1, privileges.size());

        // Revoke ALL from the table should now remove all the column privileges.
        privilege.setAction(AccessConstants.ALL);
        privilege.setCreateTime(System.currentTimeMillis());
        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);
        role = sentryStore.getMSentryRoleByName(roleName);
        privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 0, privileges.size());
    }

    /**
     * Regression test for SENTRY-552
     */
    @Test
    public void testGrantRevokeTablePrivilegeDowngradeByDb() throws Exception {
        String roleName = "test-table-db-downgrade-privilege";
        String server = "server1";
        String db = "db1";
        String table1 = "tbl1";
        String table2 = "tbl2";
        createRole(roleName);
        TSentryPrivilege privilegeTable1 = new TSentryPrivilege();
        privilegeTable1.setPrivilegeScope("TABLE");
        privilegeTable1.setServerName(server);
        privilegeTable1.setDbName(db);
        privilegeTable1.setTableName(table1);
        privilegeTable1.setAction(AccessConstants.ALL);
        privilegeTable1.setCreateTime(System.currentTimeMillis());
        TSentryPrivilege privilegeTable2 = privilegeTable1.deepCopy();
        privilegeTable2.setTableName(table2);

        // Grant ALL on table1 and table2
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilegeTable1),
                null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilegeTable2),
                null);
        MSentryRole role = sentryStore.getMSentryRoleByName(roleName);
        Set<MSentryPrivilege> privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 2, privileges.size());

        // Revoke SELECT on table2
        privilegeTable2.setAction(AccessConstants.SELECT);
        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName,
                Sets.newHashSet(privilegeTable2), null);
        // after having ALL and revoking SELECT, we should have (INSERT) at table2
        role = sentryStore.getMSentryRoleByName(roleName);
        privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 2, privileges.size());

        for (MSentryPrivilege mPrivilege : privileges) {
            assertEquals(server, mPrivilege.getServerName());
            assertEquals(db, mPrivilege.getDbName());
            assertFalse(mPrivilege.getGrantOption());
            if (mPrivilege.getTableName().equals(table1)) {
                assertEquals(AccessConstants.ALL, mPrivilege.getAction());
            } else if (mPrivilege.getTableName().equals(table2)) {
                assertNotSame(AccessConstants.SELECT, mPrivilege.getAction());
                assertNotSame(AccessConstants.ALL, mPrivilege.getAction());
            } else {
                fail("Unexpected table name: " + mPrivilege.getTableName());
            }
        }

        // Revoke INSERT on Database
        privilegeTable2.setAction(AccessConstants.INSERT);
        privilegeTable2.setPrivilegeScope("DATABASE");
        privilegeTable2.unsetTableName();
        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName,
                Sets.newHashSet(privilegeTable2), null);
        role = sentryStore.getMSentryRoleByName(roleName);
        privileges = role.getPrivileges();

        // after revoking INSERT database level privilege
        // table1 should have (SELECT)
        // table2 should have ()
        assertEquals(privileges.toString(), 1, privileges.size());
        for (MSentryPrivilege mPrivilege : privileges) {
            assertEquals(server, mPrivilege.getServerName());
            assertEquals(db, mPrivilege.getDbName());
            if (table1.equals(mPrivilege.getTableName())) {
                assertNotSame(AccessConstants.INSERT, mPrivilege.getAction());
                assertNotSame(AccessConstants.ALL, mPrivilege.getAction());
            } else if (table2.equals(mPrivilege.getTableName())) {
                assertNotSame(AccessConstants.INSERT, mPrivilege.getAction());
                assertNotSame(AccessConstants.SELECT, mPrivilege.getAction());
                assertNotSame(AccessConstants.ALL, mPrivilege.getAction());
            }
            assertFalse(mPrivilege.getGrantOption());
        }
    }

    /**
     * Regression test for SENTRY-552
     */
    @Test
    public void testGrantRevokeColumnPrivilegeDowngradeByDb() throws Exception {
        String roleName = "test-column-db-downgrade-privilege";
        String server = "server1";
        String db = "db1";
        String table = "tbl1";
        String column1 = "c1";
        String column2 = "c2";
        createRole(roleName);
        TSentryPrivilege privilegeCol1 = new TSentryPrivilege();
        privilegeCol1.setPrivilegeScope("COLUMN");
        privilegeCol1.setServerName(server);
        privilegeCol1.setDbName(db);
        privilegeCol1.setTableName(table);
        privilegeCol1.setColumnName(column1);
        privilegeCol1.setAction(AccessConstants.ALL);
        privilegeCol1.setCreateTime(System.currentTimeMillis());
        TSentryPrivilege privilegeCol2 = privilegeCol1.deepCopy();
        privilegeCol2.setColumnName(column2);

        // Grant ALL on column1 and column2
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilegeCol1),
                null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilegeCol2),
                null);
        MSentryRole role = sentryStore.getMSentryRoleByName(roleName);
        Set<MSentryPrivilege> privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 2, privileges.size());

        // Revoke SELECT on column2
        privilegeCol2.setAction(AccessConstants.SELECT);
        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilegeCol2),
                null);
        // after having ALL and revoking SELECT, we should have (INSERT)
        role = sentryStore.getMSentryRoleByName(roleName);
        privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 2, privileges.size());

        for (MSentryPrivilege mPrivilege : privileges) {
            assertEquals(server, mPrivilege.getServerName());
            assertEquals(db, mPrivilege.getDbName());
            assertEquals(table, mPrivilege.getTableName());
            assertFalse(mPrivilege.getGrantOption());
            if (mPrivilege.getColumnName().equals(column1)) {
                assertEquals(AccessConstants.ALL, mPrivilege.getAction());
            } else if (mPrivilege.getColumnName().equals(column2)) {
                assertNotSame(AccessConstants.SELECT, mPrivilege.getAction());
            } else {
                fail("Unexpected column name: " + mPrivilege.getColumnName());
            }
        }

        // Revoke INSERT on Database
        privilegeCol2.setAction(AccessConstants.INSERT);
        privilegeCol2.setPrivilegeScope("DATABASE");
        privilegeCol2.unsetTableName();
        privilegeCol2.unsetColumnName();
        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilegeCol2),
                null);
        role = sentryStore.getMSentryRoleByName(roleName);
        privileges = role.getPrivileges();

        // after revoking INSERT database level privilege
        // column2 has ()
        // and downgrade column1 to (SELECT) privileges.
        assertEquals(privileges.toString(), 1, privileges.size());
        for (MSentryPrivilege mPrivilege : privileges) {
            assertEquals(server, mPrivilege.getServerName());
            assertEquals(db, mPrivilege.getDbName());
            assertEquals(table, mPrivilege.getTableName());
            if (column1.equals(mPrivilege.getColumnName())) {
                assertNotSame(AccessConstants.INSERT, mPrivilege.getAction());
            } else if (column1.equals(mPrivilege.getColumnName())) {
                assertNotSame(AccessConstants.SELECT, mPrivilege.getAction());
                assertNotSame(AccessConstants.INSERT, mPrivilege.getAction());
            }
            assertFalse(mPrivilege.getGrantOption());
        }
    }

    @Test
    public void testGrantRevokePrivilegeWithGrantOption() throws Exception {
        String roleName = "test-grantOption-table";
        String server = "server1";
        String db = "db1";
        String table = "tbl1";
        TSentryGrantOption grantOption = TSentryGrantOption.TRUE;
        createRole(roleName);

        TSentryPrivilege privilege = new TSentryPrivilege();
        privilege.setPrivilegeScope("TABLE");
        privilege.setServerName(server);
        privilege.setDbName(db);
        privilege.setTableName(table);
        privilege.setAction(AccessConstants.ALL);
        privilege.setCreateTime(System.currentTimeMillis());
        privilege.setGrantOption(grantOption);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);
        MSentryRole role = sentryStore.getMSentryRoleByName(roleName);
        Set<MSentryPrivilege> privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 1, privileges.size());
        assertEquals(Boolean.valueOf(privilege.getGrantOption().toString()),
                Iterables.get(privileges, 0).getGrantOption());
        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);
        role = sentryStore.getMSentryRoleByName(roleName);
        privileges = role.getPrivileges();
        assertEquals(0, privileges.size());

        roleName = "test-grantOption-db";

        createRole(roleName);
        privilege = new TSentryPrivilege();
        privilege.setPrivilegeScope("DATABASE");
        privilege.setServerName(server);
        privilege.setDbName(db);
        privilege.setAction(AccessConstants.ALL);
        privilege.setGrantOption(TSentryGrantOption.TRUE);
        privilege.setCreateTime(System.currentTimeMillis());
        privilege.setGrantOption(grantOption);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);
        role = sentryStore.getMSentryRoleByName(roleName);
        privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 1, privileges.size());

        privilege.setAction(AccessConstants.SELECT);
        privilege.setGrantOption(TSentryGrantOption.UNSET);
        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);
        // after having ALL and revoking SELECT, we should have (INSERT)
        role = sentryStore.getMSentryRoleByName(roleName);
        privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 1, privileges.size());
        for (MSentryPrivilege mPrivilege : privileges) {
            assertEquals(server, mPrivilege.getServerName());
            assertEquals(db, mPrivilege.getDbName());
            assertNotSame(AccessConstants.SELECT, mPrivilege.getAction());
        }
    }

    @Test
    public void testRevokeAllGrantOption() throws Exception {
        // 1. set local group mapping
        // user0->group0->role0
        String grantor = "g1";
        String[] users = { "user0" };
        String[] roles = { "role0" };
        String[] groups = { "group0" };
        for (int i = 0; i < users.length; i++) {
            addGroupsToUser(users[i], groups[i]);
            sentryStore.createSentryRole(roles[i]);
            Set<TSentryGroup> tGroups = Sets.newHashSet();
            TSentryGroup tGroup = new TSentryGroup(groups[i]);
            tGroups.add(tGroup);
            sentryStore.alterSentryRoleAddGroups(grantor, roles[i], tGroups);
        }
        writePolicyFile();

        // 2. g1 grant select on table tb1 to role0, with grant option
        String server = "server1";
        String db = "db1";
        String table = "tbl1";
        String roleName = roles[0];
        TSentryPrivilege privilege = new TSentryPrivilege();
        privilege.setPrivilegeScope("TABLE");
        privilege.setServerName(server);
        privilege.setDbName(db);
        privilege.setTableName(table);
        privilege.setAction(AccessConstants.SELECT);
        privilege.setCreateTime(System.currentTimeMillis());
        privilege.setGrantOption(TSentryGrantOption.TRUE);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);

        // 3. g1 grant select on table tb1 to role0, no grant option
        roleName = roles[0];
        privilege.setGrantOption(TSentryGrantOption.FALSE);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);

        // 4. g1 revoke all privilege from role0
        roleName = roles[0];
        privilege.setGrantOption(TSentryGrantOption.UNSET);
        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);
        MSentryRole role = sentryStore.getMSentryRoleByName(roleName);
        Set<MSentryPrivilege> privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 0, privileges.size());
    }

    @Test
    public void testGrantDuplicatePrivilege() throws Exception {
        String roleName = "test-privilege";
        String server = "server1";
        String db = "db1";
        String table = "tbl1";
        createRole(roleName);
        TSentryPrivilege privilege = new TSentryPrivilege();
        privilege.setPrivilegeScope("TABLE");
        privilege.setServerName(server);
        privilege.setDbName(db);
        privilege.setTableName(table);
        privilege.setAction(AccessConstants.ALL);
        privilege.setCreateTime(System.currentTimeMillis());
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);
        privilege.setServerName("Server1");
        privilege.setDbName("DB1");
        privilege.setTableName("TBL1");
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);
        MSentryRole role = sentryStore.getMSentryRoleByName(roleName);
        Set<MSentryPrivilege> privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 1, privileges.size());
    }

    @Test
    public void testListSentryPrivilegesForProvider() throws Exception {
        String roleName1 = "list-privs-r1", roleName2 = "list-privs-r2";
        String groupName1 = "list-privs-g1", groupName2 = "list-privs-g2";
        String userName1 = "list-privs-u1", userName2 = "list-privs-u2";
        String userWithoutRole = "user-no-privs";
        Set<String> noRoleUsers = Sets.newHashSet(userWithoutRole);
        String grantor = "g1";
        sentryStore.createSentryRole(roleName1);
        sentryStore.createSentryRole(roleName2);
        TSentryPrivilege privilege1 = new TSentryPrivilege();
        privilege1.setPrivilegeScope("TABLE");
        privilege1.setServerName("server1");
        privilege1.setDbName("db1");
        privilege1.setTableName("tbl1");
        privilege1.setAction("SELECT");
        privilege1.setCreateTime(System.currentTimeMillis());
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1, Sets.newHashSet(privilege1),
                null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName2, Sets.newHashSet(privilege1),
                null);
        TSentryPrivilege privilege2 = new TSentryPrivilege();
        privilege2.setPrivilegeScope("SERVER");
        privilege2.setServerName("server1");
        privilege2.setCreateTime(System.currentTimeMillis());
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName2, Sets.newHashSet(privilege2),
                null);
        Set<TSentryGroup> groups = Sets.newHashSet();
        Set<String> users = Sets.newHashSet();
        TSentryGroup group = new TSentryGroup();
        group.setGroupName(groupName1);
        groups.add(group);
        users.add(userName1);
        sentryStore.alterSentryRoleAddGroups(grantor, roleName1, groups);
        sentryStore.alterSentryRoleAddUsers(roleName1, users);
        groups.clear();
        users.clear();
        group = new TSentryGroup();
        group.setGroupName(groupName2);
        groups.add(group);
        users.add(userName2);
        // group 2 and user2 has both roles 1 and 2
        sentryStore.alterSentryRoleAddGroups(grantor, roleName1, groups);
        sentryStore.alterSentryRoleAddGroups(grantor, roleName2, groups);
        sentryStore.alterSentryRoleAddUsers(roleName1, users);
        sentryStore.alterSentryRoleAddUsers(roleName2, users);
        // group1 all roles
        assertEquals(Sets.newHashSet("server=server1->db=db1->table=tbl1->action=select"),
                SentryStore
                        .toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(groupName1),
                                noRoleUsers, new TSentryActiveRoleSet(true, new HashSet<String>()))));
        // user1 all roles
        assertEquals(Sets.newHashSet("server=server1->db=db1->table=tbl1->action=select"),
                SentryStore.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(""),
                        Sets.newHashSet(userName1), new TSentryActiveRoleSet(true, new HashSet<String>()))));
        // group1 and user1 all roles
        assertEquals(Sets.newHashSet("server=server1->db=db1->table=tbl1->action=select"),
                SentryStore.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(
                        Sets.newHashSet(groupName1), Sets.newHashSet(userName1),
                        new TSentryActiveRoleSet(true, new HashSet<String>()))));
        // one active role
        assertEquals(Sets.newHashSet("server=server1->db=db1->table=tbl1->action=select"),
                SentryStore
                        .toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(groupName1),
                                noRoleUsers, new TSentryActiveRoleSet(false, Sets.newHashSet(roleName1)))));
        // unknown active role
        assertEquals(Sets.newHashSet(),
                SentryStore
                        .toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(groupName1),
                                noRoleUsers, new TSentryActiveRoleSet(false, Sets.newHashSet("not a role")))));
        // no active roles
        assertEquals(Sets.newHashSet(), SentryStore.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(
                Sets.newHashSet(groupName1), noRoleUsers, new TSentryActiveRoleSet(false, new HashSet<String>()))));

        // group2 all roles
        assertEquals(Sets.newHashSet("server=server1->db=db1->table=tbl1->action=select", "server=server1"),
                SentryStore
                        .toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(groupName2),
                                Sets.newHashSet(""), new TSentryActiveRoleSet(true, new HashSet<String>()))));
        // user2 all roles
        assertEquals(Sets.newHashSet("server=server1->db=db1->table=tbl1->action=select", "server=server1"),
                SentryStore.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(""),
                        Sets.newHashSet(userName2), new TSentryActiveRoleSet(true, new HashSet<String>()))));
        // user2 and group2 all roles
        assertEquals(Sets.newHashSet("server=server1->db=db1->table=tbl1->action=select", "server=server1"),
                SentryStore.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(
                        Sets.newHashSet(groupName2), Sets.newHashSet(userName2),
                        new TSentryActiveRoleSet(true, new HashSet<String>()))));

        // one active role
        assertEquals(Sets.newHashSet("server=server1->db=db1->table=tbl1->action=select"),
                SentryStore
                        .toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(groupName2),
                                noRoleUsers, new TSentryActiveRoleSet(false, Sets.newHashSet(roleName1)))));
        assertEquals(Sets.newHashSet("server=server1->db=db1->table=tbl1->action=select", "server=server1"),
                SentryStore
                        .toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(groupName2),
                                noRoleUsers, new TSentryActiveRoleSet(false, Sets.newHashSet(roleName2)))));
        // unknown active role
        assertEquals(Sets.newHashSet(),
                SentryStore
                        .toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(groupName2),
                                noRoleUsers, new TSentryActiveRoleSet(false, Sets.newHashSet("not a role")))));
        // no active roles
        assertEquals(Sets.newHashSet(), SentryStore.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(
                Sets.newHashSet(groupName2), noRoleUsers, new TSentryActiveRoleSet(false, new HashSet<String>()))));

        // both groups, all active roles
        assertEquals(Sets.newHashSet("server=server1->db=db1->table=tbl1->action=select", "server=server1"),
                SentryStore.toTrimedLower(
                        sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(groupName1, groupName2),
                                noRoleUsers, new TSentryActiveRoleSet(true, new HashSet<String>()))));
        // both users and groups, all active roles
        assertEquals(Sets.newHashSet("server=server1->db=db1->table=tbl1->action=select", "server=server1"),
                SentryStore.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(
                        Sets.newHashSet(groupName1, groupName2), Sets.newHashSet(userName1, userName2),
                        new TSentryActiveRoleSet(true, new HashSet<String>()))));
        // one active role
        assertEquals(Sets.newHashSet("server=server1->db=db1->table=tbl1->action=select"),
                SentryStore.toTrimedLower(
                        sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(groupName1, groupName2),
                                noRoleUsers, new TSentryActiveRoleSet(false, Sets.newHashSet(roleName1)))));
        assertEquals(Sets.newHashSet("server=server1->db=db1->table=tbl1->action=select", "server=server1"),
                SentryStore.toTrimedLower(
                        sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(groupName1, groupName2),
                                noRoleUsers, new TSentryActiveRoleSet(false, Sets.newHashSet(roleName2)))));
        // unknown active role
        assertEquals(Sets.newHashSet(),
                SentryStore.toTrimedLower(
                        sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(groupName1, groupName2),
                                noRoleUsers, new TSentryActiveRoleSet(false, Sets.newHashSet("not a role")))));
        // no active roles
        assertEquals(Sets.newHashSet(),
                SentryStore.toTrimedLower(
                        sentryStore.listAllSentryPrivilegesForProvider(Sets.newHashSet(groupName1, groupName2),
                                noRoleUsers, new TSentryActiveRoleSet(false, new HashSet<String>()))));
    }

    @Test
    public void testListRole() throws Exception {
        String roleName1 = "role1", roleName2 = "role2", roleName3 = "role3";
        String group1 = "group1", group2 = "group2";
        String grantor = "g1";

        sentryStore.createSentryRole(roleName1);
        sentryStore.createSentryRole(roleName2);
        sentryStore.createSentryRole(roleName3);

        sentryStore.alterSentryRoleAddGroups(grantor, roleName1, Sets.newHashSet(new TSentryGroup(group1)));
        sentryStore.alterSentryRoleAddGroups(grantor, roleName2, Sets.newHashSet(new TSentryGroup(group2)));
        sentryStore.alterSentryRoleAddGroups(grantor, roleName3,
                Sets.newHashSet(new TSentryGroup(group1), new TSentryGroup(group2)));

        assertEquals(2, sentryStore.getTSentryRolesByGroupName(Sets.newHashSet(group1), false).size());
        assertEquals(2, sentryStore.getTSentryRolesByGroupName(Sets.newHashSet(group2), false).size());
        assertEquals(3, sentryStore.getTSentryRolesByGroupName(Sets.newHashSet(group1, group2), false).size());
        assertEquals(0, sentryStore.getTSentryRolesByGroupName(Sets.newHashSet("foo"), true).size());
    }

    /**
     * Assign multiple table and SERVER privileges to roles and users
     * drop privilege for the object verify that it's removed correctly
     * @throws Exception
     */
    @Test
    public void testDropDbObject() throws Exception {
        String roleName1 = "list-privs-r1", roleName2 = "list-privs-r2", roleName3 = "list-privs-r3";
        String userName1 = "user-1", userName2 = "user-2";
        sentryStore.createSentryRole(roleName1);
        sentryStore.createSentryRole(roleName2);
        sentryStore.createSentryRole(roleName3);
        sentryStore.createSentryUser(userName1);
        sentryStore.createSentryUser(userName2);

        TSentryPrivilege privilege_tbl1 = new TSentryPrivilege();
        privilege_tbl1.setPrivilegeScope("TABLE");
        privilege_tbl1.setServerName("server1");
        privilege_tbl1.setDbName("db1");
        privilege_tbl1.setTableName("tbl1");
        privilege_tbl1.setCreateTime(System.currentTimeMillis());

        TSentryPrivilege privilege1 = new TSentryPrivilege(privilege_tbl1);
        privilege1.setAction("SELECT");

        TSentryPrivilege privilege2_1 = new TSentryPrivilege(privilege_tbl1);
        privilege2_1.setAction("INSERT");
        TSentryPrivilege privilege3_1 = new TSentryPrivilege(privilege_tbl1);
        privilege3_1.setAction("*");

        TSentryPrivilege privilege_server = new TSentryPrivilege();
        privilege_server.setPrivilegeScope("SERVER");
        privilege_server.setServerName("server1");
        privilege_server.setCreateTime(System.currentTimeMillis());

        TSentryPrivilege privilege_tbl2 = new TSentryPrivilege();
        privilege_tbl2.setPrivilegeScope("TABLE");
        privilege_tbl2.setServerName("server1");
        privilege_tbl2.setDbName("db1");
        privilege_tbl2.setTableName("tbl2");
        privilege_tbl2.setCreateTime(System.currentTimeMillis());

        TSentryPrivilege privilege2_3 = new TSentryPrivilege(privilege_tbl2);
        privilege2_3.setAction("SELECT");

        TSentryPrivilege privilege3_2 = new TSentryPrivilege(privilege_tbl2);
        privilege3_2.setAction("INSERT");

        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1, Sets.newHashSet(privilege1),
                null);

        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName2, Sets.newHashSet(privilege2_1),
                null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName2,
                Sets.newHashSet(privilege_server), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName2, Sets.newHashSet(privilege2_3),
                null);

        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName3, Sets.newHashSet(privilege3_1),
                null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName3, Sets.newHashSet(privilege3_2),
                null);

        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.USER, userName1, Sets.newHashSet(privilege1),
                null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.USER, userName2, Sets.newHashSet(privilege2_3),
                null);

        sentryStore.dropPrivilege(toTSentryAuthorizable(privilege_tbl1));
        assertEquals(0, sentryStore.getAllTSentryPrivilegesByRoleName(roleName1).size());
        assertEquals(2, sentryStore.getAllTSentryPrivilegesByRoleName(roleName2).size());
        assertEquals(1, sentryStore.getAllTSentryPrivilegesByRoleName(roleName3).size());

        try {
            sentryStore.getAllTSentryPrivilegesByUserName(userName1);
            fail("Should have received an exception");
        } catch (Exception e) {

        }
        assertEquals(1, sentryStore.getAllTSentryPrivilegesByUserName(userName2).size());
        sentryStore.dropPrivilege(toTSentryAuthorizable(privilege_tbl2));
        assertEquals(0, sentryStore.getAllTSentryPrivilegesByRoleName(roleName1).size());
        assertEquals(1, sentryStore.getAllTSentryPrivilegesByRoleName(roleName2).size());
        assertEquals(0, sentryStore.getAllTSentryPrivilegesByRoleName(roleName3).size());
        try {
            sentryStore.getAllTSentryPrivilegesByUserName(userName2);
            fail("Should have received an exception");
        } catch (Exception e) {
        }
    }

    /**
     * Grants owner privileges to role/user and updates the owner
     * and makes sure that the owner privilege is updated.
     * @throws Exception
     */
    @Test
    public void testUpdateOwnerPrivilege() throws Exception {
        String roleName1 = "list-privs-r1", roleName2 = "list-privs-r2", roleName3 = "list-privs-r3";
        String userName1 = "user1", userName2 = "user2";
        List<SentryOwnerInfo> ownerInfoList = null;
        sentryStore.createSentryRole(roleName1);
        sentryStore.createSentryRole(roleName2);
        sentryStore.createSentryRole(roleName3);
        sentryStore.createSentryUser(userName1);
        sentryStore.createSentryUser(userName2);

        TSentryPrivilege privilege_tbl1 = new TSentryPrivilege();
        privilege_tbl1.setPrivilegeScope("TABLE");
        privilege_tbl1.setServerName("server1");
        privilege_tbl1.setDbName("db1");
        privilege_tbl1.setTableName("tbl1");
        privilege_tbl1.setCreateTime(System.currentTimeMillis());
        privilege_tbl1.setAction("OWNER");

        TSentryAuthorizable tSentryAuthorizable = new TSentryAuthorizable();
        tSentryAuthorizable.setServer("server1");
        tSentryAuthorizable.setDb("db1");
        tSentryAuthorizable.setTable("tbl1");

        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1, Sets.newHashSet(privilege_tbl1),
                null);

        assertEquals(1, sentryStore.getAllTSentryPrivilegesByRoleName(roleName1).size());
        assertEquals(0, sentryStore.getAllTSentryPrivilegesByRoleName(roleName2).size());

        ownerInfoList = sentryStore.listOwnersByAuthorizable(tSentryAuthorizable);
        assertEquals(1, ownerInfoList.size());
        assertEquals(SentryPrincipalType.ROLE, ownerInfoList.get(0).getOwnerType());
        assertEquals(roleName1, ownerInfoList.get(0).getOwnerName());

        // Change owner from a one role to another role
        sentryStore.updateOwnerPrivilege(tSentryAuthorizable, roleName2, SentryPrincipalType.ROLE, null);
        ownerInfoList = sentryStore.listOwnersByAuthorizable(tSentryAuthorizable);
        assertEquals(1, ownerInfoList.size());
        assertEquals(SentryPrincipalType.ROLE, ownerInfoList.get(0).getOwnerType());
        assertEquals(roleName2, ownerInfoList.get(0).getOwnerName());

        assertEquals(0, sentryStore.getAllTSentryPrivilegesByRoleName(roleName1).size());
        assertEquals(1, sentryStore.getAllTSentryPrivilegesByRoleName(roleName2).size());

        tSentryAuthorizable.setTable("tbl2");
        TSentryPrivilege privilege_tbl2 = new TSentryPrivilege(privilege_tbl1);
        privilege_tbl2.setTableName("tbl2");

        // Change owner from a one user to another user
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.USER, userName1, Sets.newHashSet(privilege_tbl2),
                null);
        assertEquals(1, sentryStore.getAllTSentryPrivilegesByUserName(userName1).size());

        sentryStore.updateOwnerPrivilege(tSentryAuthorizable, userName2, SentryPrincipalType.USER, null);
        assertEquals(1, sentryStore.getAllTSentryPrivilegesByUserName(userName2).size());
        ownerInfoList = sentryStore.listOwnersByAuthorizable(tSentryAuthorizable);
        assertEquals(1, ownerInfoList.size());
        assertEquals(SentryPrincipalType.USER, ownerInfoList.get(0).getOwnerType());
        assertEquals(userName2, ownerInfoList.get(0).getOwnerName());

        // Change owner from a user to role
        sentryStore.updateOwnerPrivilege(tSentryAuthorizable, roleName1, SentryPrincipalType.ROLE, null);
        assertEquals(1, sentryStore.getAllTSentryPrivilegesByRoleName(roleName1).size());

        // At this point  roleName1 has owner privilege on db1.tb2
        //Add all privilege to roleName1 and make sure that owner privilege is not effected.
        TSentryPrivilege privilege_tbl2_all = new TSentryPrivilege(privilege_tbl2);
        privilege_tbl2_all.setAction(AccessConstants.ALL);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(privilege_tbl2_all), null);
        // Verify that there are two privileges.
        assertEquals(2, sentryStore.getAllTSentryPrivilegesByRoleName(roleName1).size());

        tSentryAuthorizable.setTable("tbl3");
        TSentryPrivilege privilege_tbl3_all = new TSentryPrivilege(privilege_tbl2);
        privilege_tbl3_all.setAction(AccessConstants.ALL);
        privilege_tbl3_all.setTableName("tbl3");
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName3,
                Sets.newHashSet(privilege_tbl3_all), null);
        assertEquals(1, sentryStore.getAllTSentryPrivilegesByRoleName(roleName3).size());
        TSentryPrivilege privilege_tbl3_owner = new TSentryPrivilege(privilege_tbl3_all);
        privilege_tbl3_owner.setAction(AccessConstants.OWNER);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName3,
                Sets.newHashSet(privilege_tbl3_owner), null);

        assertEquals(2, sentryStore.getAllTSentryPrivilegesByRoleName(roleName3).size());
    }

    @Test
    public void testListSentryOwnerPrivilegesByAuthorizable() throws Exception {
        String roleName1 = "list-privs-r1";
        String userName1 = "user1";
        sentryStore.createSentryRole(roleName1);
        sentryStore.createSentryUser(userName1);

        TSentryPrivilege privilege_tbl1 = new TSentryPrivilege();
        privilege_tbl1.setPrivilegeScope("TABLE");
        privilege_tbl1.setServerName("server1");
        privilege_tbl1.setDbName("db1");
        privilege_tbl1.setTableName("tbl1");
        privilege_tbl1.setCreateTime(System.currentTimeMillis());
        privilege_tbl1.setAction("OWNER");
        privilege_tbl1.setGrantOption(TSentryGrantOption.TRUE);

        TSentryAuthorizable tSentryAuthorizable = new TSentryAuthorizable();
        tSentryAuthorizable.setServer("server1");
        tSentryAuthorizable.setDb("db1");
        tSentryAuthorizable.setTable("tbl1");

        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1, Sets.newHashSet(privilege_tbl1),
                null);

        sentryStore.updateOwnerPrivilege(tSentryAuthorizable, userName1, SentryPrincipalType.USER, null);
        assertEquals(1, sentryStore.getAllTSentryPrivilegesByUserName(userName1).size());
    }

    @Test
    public void testRevokeOwnerPrivilege() throws Exception {

        String roleName1 = "list-privs-r1";
        String userName1 = "user1";
        sentryStore.createSentryRole(roleName1);
        sentryStore.createSentryUser(userName1);

        TSentryPrivilege privilege_tbl1 = new TSentryPrivilege();
        privilege_tbl1.setPrivilegeScope("TABLE");
        privilege_tbl1.setServerName("server1");
        privilege_tbl1.setDbName("db1");
        privilege_tbl1.setTableName("tbl1");
        privilege_tbl1.setCreateTime(System.currentTimeMillis());
        privilege_tbl1.setAction("OWNER");
        privilege_tbl1.setGrantOption(TSentryGrantOption.TRUE);

        TSentryAuthorizable tSentryAuthorizable = new TSentryAuthorizable();
        tSentryAuthorizable.setServer("server1");
        tSentryAuthorizable.setDb("db1");
        tSentryAuthorizable.setTable("tbl1");

        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1, Sets.newHashSet(privilege_tbl1),
                null);

        sentryStore.revokeOwnerPrivileges(tSentryAuthorizable, null);
        assertEquals(0, sentryStore.getAllTSentryPrivilegesByUserName(userName1).size());
    }

    @Test
    public void testgetPrivilegesForAuthorizables() throws Exception {

        String roleName1 = "list-privs-r1";
        String userName1 = "user1";
        String uri1 = "file:///var/folders/dt/9zm44z9s6bjfxbrm4v36lzdc0000gp/T/1401860678102-0/data/kv1.dat";

        sentryStore.createSentryRole(roleName1);
        sentryStore.createSentryUser(userName1);

        TSentryPrivilege privilege_tbl1 = new TSentryPrivilege();
        privilege_tbl1.setPrivilegeScope("TABLE");
        privilege_tbl1.setServerName("server1");
        privilege_tbl1.setDbName("db1");
        privilege_tbl1.setTableName("tbl1");
        privilege_tbl1.setCreateTime(System.currentTimeMillis());
        privilege_tbl1.setAction("SELECT");
        privilege_tbl1.setGrantOption(TSentryGrantOption.TRUE);

        TSentryAuthorizable tSentryAuthorizable = new TSentryAuthorizable();
        tSentryAuthorizable.setServer("server1");
        tSentryAuthorizable.setDb("db1");
        tSentryAuthorizable.setTable("tbl1");

        TSentryPrivilege privilege_tbl2 = new TSentryPrivilege();
        privilege_tbl2.setPrivilegeScope("TABLE");
        privilege_tbl2.setServerName("server1");
        privilege_tbl2.setDbName("db1");
        privilege_tbl2.setTableName("tbl2");
        privilege_tbl2.setCreateTime(System.currentTimeMillis());
        privilege_tbl2.setAction("SELECT");
        privilege_tbl2.setGrantOption(TSentryGrantOption.TRUE);

        TSentryAuthorizable tSentryAuthorizable2 = new TSentryAuthorizable();
        tSentryAuthorizable2.setServer("server1");
        tSentryAuthorizable2.setDb("db1");
        tSentryAuthorizable2.setTable("tbl2");

        TSentryPrivilege privilege_col1 = new TSentryPrivilege(privilege_tbl2);
        privilege_col1.setPrivilegeScope("TABLE");
        privilege_col1.setServerName("server1");
        privilege_col1.setDbName("db3");
        privilege_col1.setTableName("tbl3");
        privilege_col1.setCreateTime(System.currentTimeMillis());
        privilege_col1.setAction("SELECT");
        privilege_col1.setGrantOption(TSentryGrantOption.TRUE);
        privilege_col1.setColumnName("Column1");

        TSentryAuthorizable tSentryAuthorizable3 = new TSentryAuthorizable();
        tSentryAuthorizable3.setServer("server1");
        tSentryAuthorizable3.setDb("db3");
        tSentryAuthorizable3.setTable("tbl3");
        tSentryAuthorizable3.setColumn("Column1");

        TSentryPrivilege tSentryUriPrivilege = new TSentryPrivilege("URI", "server1", "ALL");
        tSentryUriPrivilege.setURI(uri1);

        TSentryAuthorizable tSentryUriAuthorizable = new TSentryAuthorizable();
        tSentryUriAuthorizable.setUri(uri1);
        tSentryUriAuthorizable.setServer("server1");

        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(privilege_tbl1, privilege_tbl2, privilege_col1, tSentryUriPrivilege), null);

        // Verify the privilege count
        List<MSentryPrivilege> privilege = sentryStore.getPrivilegesForAuthorizables(Lists.newArrayList(
                tSentryAuthorizable, tSentryAuthorizable2, tSentryUriAuthorizable, tSentryAuthorizable3));
        assertEquals(4, privilege.size());

        // Verify the behavior when the empty authorizable list provided.
        privilege = sentryStore.getPrivilegesForAuthorizables(Collections.EMPTY_LIST);
        assertNotNull(privilege);
        assertEquals(4, privilege.size());

        // Verify the behvaior when the authorizable list is null.
        privilege = sentryStore.getPrivilegesForAuthorizables(null);
        assertNotNull(privilege);
        assertEquals(4, privilege.size());

        // Verify the privilege granted to URI is returned
        privilege = sentryStore.getPrivilegesForAuthorizables(Lists.newArrayList(tSentryAuthorizable2));
        assertEquals(1, privilege.size());

        TSentryAuthorizable tSentrDbAuthorizable = new TSentryAuthorizable();
        tSentrDbAuthorizable.setServer("server1");
        tSentrDbAuthorizable.setDb("db1");

        // Verify that all the privileges granted to tables in database db1 are returned.
        privilege = sentryStore.getPrivilegesForAuthorizables(Lists.newArrayList(tSentrDbAuthorizable));
        assertEquals(2, privilege.size());

        // Verify that all the privileges granted to server are returned.
        privilege = sentryStore.getPrivilegesForAuthorizables(Lists.newArrayList(tSentryAuthorizable3));
        assertEquals(1, privilege.size());

        TSentryAuthorizable tSentrServerAuthorizable = new TSentryAuthorizable();
        tSentrServerAuthorizable.setServer("server1");

        // Verify that all the privileges granted to server are returned.
        privilege = sentryStore.getPrivilegesForAuthorizables(Lists.newArrayList(tSentrServerAuthorizable));
        assertEquals(4, privilege.size());

        // Grant user1 same privileges granted to role1 and make sure that there are no duplicates in the privileges.
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.USER, userName1,
                Sets.newHashSet(privilege_tbl1, privilege_tbl2, tSentryUriPrivilege), null);

        // Verify that all the privileges granted to server are returned.
        privilege = sentryStore.getPrivilegesForAuthorizables(Lists.newArrayList(tSentrServerAuthorizable));
        assertEquals(4, privilege.size());
    }

    @Test
    public void testgetPrivilegesForURIs() throws Exception {

        String roleName1 = "list-privs-r1";
        String uriPrefix = "file:///var/folders/dt/9zm44z9s6bjfxbrm4v36lzdc0000gp/T/1401860678102-0/";
        String uri1 = "file:///var/folders/dt/9zm44z9s6bjfxbrm4v36lzdc0000gp/T/1401860678102-0/data/kv1.dat";
        String uri2 = "file:///var/folders/dt/9zm44z9s6bjfxbrm4v36lzdc0000gp/T/1401860678102-0/data/kv2.dat";

        sentryStore.createSentryRole(roleName1);

        TSentryPrivilege tSentryUriPrivilege1 = new TSentryPrivilege("URI", "server1", "ALL");
        tSentryUriPrivilege1.setURI(uri1);

        TSentryPrivilege tSentryUriPrivilege2 = new TSentryPrivilege("URI", "server1", "ALL");
        tSentryUriPrivilege2.setURI(uri2);

        TSentryAuthorizable tSentryUriAuthorizable = new TSentryAuthorizable();
        tSentryUriAuthorizable.setUri(uriPrefix);
        tSentryUriAuthorizable.setServer("server1");

        // Grant user1 same privileges granted to role1 and make sure that there are no duplicates in the privileges.
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(tSentryUriPrivilege1, tSentryUriPrivilege2), null);

        // Verify that all the privileges granted to all the URI;s under given prefix are returned.
        List<MSentryPrivilege> privilege = sentryStore
                .getPrivilegesForAuthorizables(Lists.newArrayList(tSentryUriAuthorizable));
        assertEquals(2, privilege.size());
    }

    @Test
    public void testDropUserOnUpdateOwnerPrivilege() throws Exception {
        String userName1 = "user1", userName2 = "user2";
        sentryStore.createSentryUser(userName1);
        sentryStore.createSentryUser(userName2);

        TSentryPrivilege privilege_tbl1 = new TSentryPrivilege();
        privilege_tbl1.setPrivilegeScope("TABLE");
        privilege_tbl1.setServerName("server1");
        privilege_tbl1.setDbName("db1");
        privilege_tbl1.setTableName("tbl1");
        privilege_tbl1.setCreateTime(System.currentTimeMillis());
        privilege_tbl1.setAction("OWNER");

        TSentryAuthorizable tSentryAuthorizable = new TSentryAuthorizable();
        tSentryAuthorizable.setServer("server1");
        tSentryAuthorizable.setDb("db1");
        tSentryAuthorizable.setTable("tbl1");

        // Change owner from a one user to another user
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.USER, userName1, Sets.newHashSet(privilege_tbl1),
                null);
        assertEquals(1, sentryStore.getAllTSentryPrivilegesByUserName(userName1).size());

        sentryStore.updateOwnerPrivilege(tSentryAuthorizable, userName2, SentryPrincipalType.USER, null);
        assertEquals(1, sentryStore.getAllTSentryPrivilegesByUserName(userName2).size());

        try {
            sentryStore.createSentryUser(userName1);
        } catch (Exception e) {
            fail("Exception should not be seen asthe user: " + userName1 + " should have been deleted.");
        }

    }

    /**
     * Regression test for SENTRY-547 and SENTRY-548
     * Use case:
     * GRANT INSERT on TABLE tbl1 to ROLE role1
     * GRANT SELECT on TABLE tbl1 to ROLE role1
     * GRANT ALTER on TABLE tbl1 to ROLE role1
     * GRANT DROP on TABLE tbl1 to ROLE role1
     * DROP TABLE tbl1
     *
     * After drop tbl1, role1 should have 0 privileges
     */
    @Test
    public void testDropTableWithMultiAction() throws Exception {
        String roleName1 = "role1";
        sentryStore.createSentryRole(roleName1);

        TSentryPrivilege privilege_tbl1 = new TSentryPrivilege();
        privilege_tbl1.setPrivilegeScope("TABLE");
        privilege_tbl1.setServerName("server1");
        privilege_tbl1.setDbName("db1");
        privilege_tbl1.setTableName("tbl1");
        privilege_tbl1.setCreateTime(System.currentTimeMillis());

        TSentryPrivilege privilege_tbl1_insert = new TSentryPrivilege(privilege_tbl1);
        privilege_tbl1_insert.setAction(AccessConstants.INSERT);

        TSentryPrivilege privilege_tbl1_select = new TSentryPrivilege(privilege_tbl1);
        privilege_tbl1_select.setAction(AccessConstants.SELECT);

        TSentryPrivilege privilege_tbl1_alter = new TSentryPrivilege(privilege_tbl1);
        privilege_tbl1_alter.setAction(AccessConstants.ALTER);

        TSentryPrivilege privilege_tbl1_drop = new TSentryPrivilege(privilege_tbl1);
        privilege_tbl1_drop.setAction(AccessConstants.DROP);

        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(privilege_tbl1_insert), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(privilege_tbl1_select), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(privilege_tbl1_alter), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(privilege_tbl1_drop), null);

        assertEquals(4, sentryStore.getAllTSentryPrivilegesByRoleName(roleName1).size());

        // after drop privilege_tbl1, role1 should have 0 privileges
        sentryStore.dropPrivilege(toTSentryAuthorizable(privilege_tbl1));
        assertEquals(0, sentryStore.getAllTSentryPrivilegesByRoleName(roleName1).size());
    }

    @Test
    public void testDropTableWithColumn() throws Exception {
        String roleName1 = "role1", roleName2 = "role2";
        String table1 = "tbl1";

        sentryStore.createSentryRole(roleName1);
        sentryStore.createSentryRole(roleName2);

        TSentryPrivilege privilege_tbl1 = new TSentryPrivilege();
        privilege_tbl1.setPrivilegeScope("TABLE");
        privilege_tbl1.setServerName("server1");
        privilege_tbl1.setDbName("db1");
        privilege_tbl1.setTableName(table1);
        privilege_tbl1.setAction(AccessConstants.SELECT);
        privilege_tbl1.setCreateTime(System.currentTimeMillis());

        TSentryPrivilege privilege_tbl1_c1 = new TSentryPrivilege(privilege_tbl1);
        privilege_tbl1_c1.setPrivilegeScope("COLUMN");
        privilege_tbl1_c1.setColumnName("c1");
        privilege_tbl1_c1.setCreateTime(System.currentTimeMillis());

        TSentryPrivilege privilege_tbl1_c2 = new TSentryPrivilege(privilege_tbl1);
        privilege_tbl1_c2.setPrivilegeScope("COLUMN");
        privilege_tbl1_c2.setColumnName("c2");
        privilege_tbl1_c2.setCreateTime(System.currentTimeMillis());

        TSentryPrivilege privilege_tbl1_c3 = new TSentryPrivilege(privilege_tbl1);
        privilege_tbl1_c3.setPrivilegeScope("COLUMN");
        privilege_tbl1_c3.setColumnName("c3");
        privilege_tbl1_c3.setCreateTime(System.currentTimeMillis());

        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(privilege_tbl1_c1), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(privilege_tbl1_c2), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName2,
                Sets.newHashSet(privilege_tbl1_c3), null);

        Set<TSentryPrivilege> privilegeSet = sentryStore.getAllTSentryPrivilegesByRoleName(roleName1);
        assertEquals(2, privilegeSet.size());
        privilegeSet = sentryStore.getAllTSentryPrivilegesByRoleName(roleName2);
        assertEquals(1, privilegeSet.size());

        TSentryAuthorizable tableAuthorizable = toTSentryAuthorizable(privilege_tbl1);
        sentryStore.dropPrivilege(tableAuthorizable);

        privilegeSet = sentryStore.getAllTSentryPrivilegesByRoleName(roleName1);
        assertEquals(0, privilegeSet.size());
        privilegeSet = sentryStore.getAllTSentryPrivilegesByRoleName(roleName2);
        assertEquals(0, privilegeSet.size());
    }

    @Test
    public void testDropOverlappedPrivileges() throws Exception {
        String roleName1 = "list-privs-r1";
        sentryStore.createSentryRole(roleName1);

        TSentryPrivilege privilege_tbl1 = new TSentryPrivilege();
        privilege_tbl1.setPrivilegeScope("TABLE");
        privilege_tbl1.setServerName("server1");
        privilege_tbl1.setDbName("db1");
        privilege_tbl1.setTableName("tbl1");
        privilege_tbl1.setCreateTime(System.currentTimeMillis());

        TSentryPrivilege privilege_tbl1_insert = new TSentryPrivilege(privilege_tbl1);
        privilege_tbl1_insert.setAction("INSERT");

        TSentryPrivilege privilege_tbl1_all = new TSentryPrivilege(privilege_tbl1);
        privilege_tbl1_all.setAction("*");

        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(privilege_tbl1_insert), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(privilege_tbl1_all), null);

        sentryStore.dropPrivilege(toTSentryAuthorizable(privilege_tbl1));
        assertEquals(0, sentryStore.getAllTSentryPrivilegesByRoleName(roleName1).size());
    }

    private TSentryAuthorizable toTSentryAuthorizable(TSentryPrivilege tSentryPrivilege) {
        TSentryAuthorizable tSentryAuthorizable = new TSentryAuthorizable();
        tSentryAuthorizable.setServer(tSentryPrivilege.getServerName());
        tSentryAuthorizable.setDb(tSentryPrivilege.getDbName());
        tSentryAuthorizable.setTable(tSentryPrivilege.getTableName());
        tSentryAuthorizable.setUri(tSentryPrivilege.getURI());
        return tSentryAuthorizable;
    }

    /***
     * Create roles and users and assign privileges for same table rename the privileges for
     * the table and verify the new privileges
     * @throws Exception
     */
    @Test
    public void testRenameTable() throws Exception {
        String roleName1 = "role1", roleName2 = "role2", roleName3 = "role3";
        String userName1 = "user-1", userName2 = "user-2", userName3 = "user-3";
        String table1 = "tbl1", table2 = "tbl2";

        sentryStore.createSentryRole(roleName1);
        sentryStore.createSentryRole(roleName2);
        sentryStore.createSentryRole(roleName3);
        sentryStore.createSentryUser(userName1);
        sentryStore.createSentryUser(userName2);
        sentryStore.createSentryUser(userName3);

        TSentryPrivilege privilege_tbl1 = new TSentryPrivilege();
        privilege_tbl1.setPrivilegeScope("TABLE");
        privilege_tbl1.setServerName("server1");
        privilege_tbl1.setDbName("db1");
        privilege_tbl1.setTableName(table1);
        privilege_tbl1.setCreateTime(System.currentTimeMillis());

        TSentryPrivilege privilege_tbl1_insert = new TSentryPrivilege(privilege_tbl1);
        privilege_tbl1_insert.setAction(AccessConstants.INSERT);

        TSentryPrivilege privilege_tbl1_select = new TSentryPrivilege(privilege_tbl1);
        privilege_tbl1_select.setAction(AccessConstants.SELECT);

        TSentryPrivilege privilege_tbl1_all = new TSentryPrivilege(privilege_tbl1);
        privilege_tbl1_all.setAction(AccessConstants.ALL);

        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(privilege_tbl1_insert), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName2,
                Sets.newHashSet(privilege_tbl1_select), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName3,
                Sets.newHashSet(privilege_tbl1_all), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.USER, userName1,
                Sets.newHashSet(privilege_tbl1_insert), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.USER, userName2,
                Sets.newHashSet(privilege_tbl1_select), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.USER, userName3,
                Sets.newHashSet(privilege_tbl1_all), null);

        TSentryAuthorizable oldTable = toTSentryAuthorizable(privilege_tbl1);
        TSentryAuthorizable newTable = toTSentryAuthorizable(privilege_tbl1);
        newTable.setTable(table2);
        sentryStore.renamePrivilege(oldTable, newTable);

        for (String roleName : Sets.newHashSet(roleName1, roleName2, roleName3)) {
            Set<TSentryPrivilege> privilegeSet = sentryStore.getAllTSentryPrivilegesByRoleName(roleName);
            assertEquals(1, privilegeSet.size());
            for (TSentryPrivilege privilege : privilegeSet) {
                assertTrue(table2.equalsIgnoreCase(privilege.getTableName()));
            }
        }
        for (String userName : Sets.newHashSet(userName1, userName2, userName3)) {
            Set<TSentryPrivilege> privilegeSet = sentryStore.getAllTSentryPrivilegesByUserName(userName);
            assertEquals(1, privilegeSet.size());
            for (TSentryPrivilege privilege : privilegeSet) {
                assertTrue(table2.equalsIgnoreCase(privilege.getTableName()));
            }
        }
    }

    /**
     * Regression test for SENTRY-550
     * Use case:
     * GRANT INSERT on TABLE tbl1 to ROLE role1
     * GRANT SELECT on TABLE tbl1 to ROLE role1
     * GRANT ALTER on TABLE tbl1 to ROLE role1
     * GRANT DROP on TABLE tbl1 to ROLE role1
     * RENAME TABLE tbl1 to tbl2
     *
     * After rename tbl1 to tbl2, table name of all role1's privileges should be "tbl2"
     */
    @Test
    public void testRenameTableWithMultiAction() throws Exception {
        String roleName1 = "role1";
        String table1 = "tbl1", table2 = "tbl2";
        sentryStore.createSentryRole(roleName1);

        TSentryPrivilege privilege_tbl1 = new TSentryPrivilege();
        privilege_tbl1.setPrivilegeScope("TABLE");
        privilege_tbl1.setServerName("server1");
        privilege_tbl1.setDbName("db1");
        privilege_tbl1.setTableName(table1);
        privilege_tbl1.setCreateTime(System.currentTimeMillis());

        TSentryPrivilege privilege_tbl1_insert = new TSentryPrivilege(privilege_tbl1);
        privilege_tbl1_insert.setAction(AccessConstants.INSERT);

        TSentryPrivilege privilege_tbl1_select = new TSentryPrivilege(privilege_tbl1);
        privilege_tbl1_select.setAction(AccessConstants.SELECT);

        TSentryPrivilege privilege_tbl1_alter = new TSentryPrivilege(privilege_tbl1);
        privilege_tbl1_alter.setAction(AccessConstants.ALTER);

        TSentryPrivilege privilege_tbl1_drop = new TSentryPrivilege(privilege_tbl1);
        privilege_tbl1_drop.setAction(AccessConstants.DROP);

        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(privilege_tbl1_insert), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(privilege_tbl1_select), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(privilege_tbl1_alter), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(privilege_tbl1_drop), null);

        TSentryAuthorizable oldTable = toTSentryAuthorizable(privilege_tbl1);
        TSentryAuthorizable newTable = toTSentryAuthorizable(privilege_tbl1);
        newTable.setTable(table2);
        sentryStore.renamePrivilege(oldTable, newTable);

        // after rename tbl1 to tbl2, all table name of role's privilege will be tbl2
        Set<TSentryPrivilege> privilegeSet = sentryStore.getAllTSentryPrivilegesByRoleName(roleName1);
        assertEquals(4, privilegeSet.size());
        for (TSentryPrivilege privilege : privilegeSet) {
            assertTrue(table2.equalsIgnoreCase(privilege.getTableName()));
        }
    }

    @Test
    public void testSentryRoleSize() throws Exception {
        for (long i = 0; i < 5; i++) {
            assertEquals((Long) i, sentryStore.getRoleCountGauge().getValue());
            sentryStore.createSentryRole("role" + i);
        }
    }

    @Test
    public void testSentryPrivilegeSize() throws Exception {
        String role1 = "role1";
        String role2 = "role2";

        sentryStore.createSentryRole(role1);
        sentryStore.createSentryRole(role2);

        TSentryPrivilege privilege = new TSentryPrivilege();
        privilege.setPrivilegeScope("TABLE");
        privilege.setServerName("server1");
        privilege.setDbName("db1");
        privilege.setTableName("tb1");
        privilege.setCreateTime(System.currentTimeMillis());

        assertEquals(Long.valueOf(0), sentryStore.getPrivilegeCountGauge().getValue());

        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, role1, Sets.newHashSet(privilege), null);
        assertEquals(Long.valueOf(1), sentryStore.getPrivilegeCountGauge().getValue());

        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, role2, Sets.newHashSet(privilege), null);
        assertEquals(Long.valueOf(1), sentryStore.getPrivilegeCountGauge().getValue());

        privilege.setTableName("tb2");
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, role2, Sets.newHashSet(privilege), null);
        assertEquals(Long.valueOf(2), sentryStore.getPrivilegeCountGauge().getValue());
    }

    @Test
    public void testSentryGroupsSize() throws Exception {
        String role1 = "role1";
        String role2 = "role2";

        sentryStore.createSentryRole(role1);
        sentryStore.createSentryRole(role2);

        Set<TSentryGroup> groups = Sets.newHashSet();
        TSentryGroup group = new TSentryGroup();
        group.setGroupName("group1");
        groups.add(group);

        String grantor = "g1";

        sentryStore.alterSentryRoleAddGroups(grantor, role1, groups);
        assertEquals(Long.valueOf(1), sentryStore.getGroupCountGauge().getValue());

        sentryStore.alterSentryRoleAddGroups(grantor, role2, groups);
        assertEquals(Long.valueOf(1), sentryStore.getGroupCountGauge().getValue());

        groups.add(new TSentryGroup("group2"));
        sentryStore.alterSentryRoleAddGroups(grantor, role2, groups);
        assertEquals(Long.valueOf(2), sentryStore.getGroupCountGauge().getValue());

    }

    @Test
    public void testSentryUsersSize() throws Exception {
        String role1 = "role1";
        String role2 = "role2";

        sentryStore.createSentryRole(role1);
        sentryStore.createSentryRole(role2);

        Set<String> users = Sets.newHashSet("user1");

        sentryStore.alterSentryRoleAddUsers(role1, users);
        assertEquals(Long.valueOf(1), sentryStore.getUserCountGauge().getValue());

        sentryStore.alterSentryRoleAddUsers(role2, users);
        assertEquals(Long.valueOf(1), sentryStore.getUserCountGauge().getValue());

        users.add("user2");
        sentryStore.alterSentryRoleAddUsers(role2, users);
        assertEquals(Long.valueOf(2), sentryStore.getUserCountGauge().getValue());

    }

    @Test
    public void testRenameTableWithColumn() throws Exception {
        String roleName1 = "role1", roleName2 = "role2";
        String table1 = "tbl1", table2 = "tbl2";

        sentryStore.createSentryRole(roleName1);
        sentryStore.createSentryRole(roleName2);

        TSentryPrivilege privilege_tbl1 = new TSentryPrivilege();
        privilege_tbl1.setPrivilegeScope("TABLE");
        privilege_tbl1.setServerName("server1");
        privilege_tbl1.setDbName("db1");
        privilege_tbl1.setTableName(table1);
        privilege_tbl1.setAction(AccessConstants.SELECT);
        privilege_tbl1.setCreateTime(System.currentTimeMillis());

        TSentryPrivilege privilege_tbl1_c1 = new TSentryPrivilege(privilege_tbl1);
        privilege_tbl1_c1.setPrivilegeScope("COLUMN");
        privilege_tbl1_c1.setColumnName("c1");
        privilege_tbl1_c1.setCreateTime(System.currentTimeMillis());

        TSentryPrivilege privilege_tbl1_c2 = new TSentryPrivilege(privilege_tbl1);
        privilege_tbl1_c2.setPrivilegeScope("COLUMN");
        privilege_tbl1_c2.setColumnName("c2");
        privilege_tbl1_c2.setCreateTime(System.currentTimeMillis());

        TSentryPrivilege privilege_tbl1_c3 = new TSentryPrivilege(privilege_tbl1);
        privilege_tbl1_c3.setPrivilegeScope("COLUMN");
        privilege_tbl1_c3.setColumnName("c3");
        privilege_tbl1_c3.setCreateTime(System.currentTimeMillis());

        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(privilege_tbl1_c1), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(privilege_tbl1_c2), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName2,
                Sets.newHashSet(privilege_tbl1_c3), null);

        Set<TSentryPrivilege> privilegeSet = sentryStore.getAllTSentryPrivilegesByRoleName(roleName1);
        assertEquals(2, privilegeSet.size());
        privilegeSet = sentryStore.getAllTSentryPrivilegesByRoleName(roleName2);
        assertEquals(1, privilegeSet.size());

        TSentryAuthorizable oldTable = toTSentryAuthorizable(privilege_tbl1);
        TSentryAuthorizable newTable = toTSentryAuthorizable(privilege_tbl1);
        newTable.setTable(table2);
        sentryStore.renamePrivilege(oldTable, newTable);

        privilegeSet = sentryStore.getAllTSentryPrivilegesByRoleName(roleName1);
        assertEquals(2, privilegeSet.size());
        for (TSentryPrivilege privilege : privilegeSet) {
            assertTrue(table2.equalsIgnoreCase(privilege.getTableName()));
        }
        privilegeSet = sentryStore.getAllTSentryPrivilegesByRoleName(roleName2);
        assertEquals(1, privilegeSet.size());
    }

    @Test
    public void testSentryTablePrivilegeSome() throws Exception {
        String roleName = "test-table-privilege-some";
        String grantor = "g1";
        String dbName = "db1";
        String table = "tb1";
        createRole(roleName);
        TSentryPrivilege tSentryPrivilege = new TSentryPrivilege("TABLE", "server1", "ALL");
        tSentryPrivilege.setDbName(dbName);
        tSentryPrivilege.setTableName(table);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName,
                Sets.newHashSet(tSentryPrivilege), null);

        TSentryAuthorizable tSentryAuthorizable = new TSentryAuthorizable();
        tSentryAuthorizable.setDb(dbName);
        tSentryAuthorizable.setTable(AccessConstants.SOME);
        tSentryAuthorizable.setServer("server1");

        Set<TSentryPrivilege> privileges = sentryStore.getTSentryPrivileges(SentryPrincipalType.ROLE,
                new HashSet<String>(Arrays.asList(roleName)), tSentryAuthorizable);

        assertTrue(privileges.size() == 1);

        Set<TSentryGroup> tSentryGroups = new HashSet<TSentryGroup>();
        tSentryGroups.add(new TSentryGroup("group1"));
        sentryStore.alterSentryRoleAddGroups(grantor, roleName, tSentryGroups);

        TSentryActiveRoleSet thriftRoleSet = new TSentryActiveRoleSet(true,
                new HashSet<String>(Arrays.asList(roleName)));

        Set<String> privs = sentryStore.listSentryPrivilegesForProvider(
                new HashSet<String>(Arrays.asList("group1")), Sets.newHashSet(grantor), thriftRoleSet,
                tSentryAuthorizable);

        assertTrue(privs.size() == 1);
        assertTrue(privs.contains("server=server1->db=" + dbName + "->table=" + table + "->action=all"));

    }

    @Test
    public void testSentryColumnPrivilegeSome() throws Exception {
        String roleName = "test-column-privilege-some";
        String grantor = "g1";
        String dbName = "db1";
        String table = "tb1";
        String column = "col1";
        createRole(roleName);
        TSentryPrivilege tSentryPrivilege = new TSentryPrivilege("TABLE", "server1", "ALL");
        tSentryPrivilege.setDbName(dbName);
        tSentryPrivilege.setTableName(table);
        tSentryPrivilege.setColumnName(column);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName,
                Sets.newHashSet(tSentryPrivilege), null);

        TSentryAuthorizable tSentryAuthorizable = new TSentryAuthorizable();
        tSentryAuthorizable.setDb(dbName);
        tSentryAuthorizable.setTable(table);
        tSentryAuthorizable.setColumn(AccessConstants.SOME);
        tSentryAuthorizable.setServer("server1");

        Set<TSentryPrivilege> privileges = sentryStore.getTSentryPrivileges(SentryPrincipalType.ROLE,
                new HashSet<String>(Arrays.asList(roleName)), tSentryAuthorizable);

        assertTrue(privileges.size() == 1);

        Set<TSentryGroup> tSentryGroups = new HashSet<TSentryGroup>();
        tSentryGroups.add(new TSentryGroup("group1"));
        sentryStore.alterSentryRoleAddGroups(grantor, roleName, tSentryGroups);

        TSentryActiveRoleSet thriftRoleSet = new TSentryActiveRoleSet(true,
                new HashSet<String>(Arrays.asList(roleName)));

        Set<String> privs = sentryStore.listSentryPrivilegesForProvider(
                new HashSet<String>(Arrays.asList("group1")), Sets.newHashSet(grantor), thriftRoleSet,
                tSentryAuthorizable);

        assertTrue(privs.size() == 1);
        assertTrue(privs.contains(
                "server=server1->db=" + dbName + "->table=" + table + "->column=" + column + "->action=all"));

    }

    @Test
    public void testSentryVersionCheck() throws Exception {
        // don't verify version, the current version willll be set in MSentryVersion
        sentryStore.verifySentryStoreSchema(false);
        assertEquals(sentryStore.getSentryVersion(), SentryStoreSchemaInfo.getSentryVersion());

        // verify the version with the same value
        sentryStore.verifySentryStoreSchema(true);

        // verify the version with the different value
        sentryStore.setSentryVersion("test-version", "test-version");
        try {
            sentryStore.verifySentryStoreSchema(true);
            fail("SentryAccessDeniedException should be thrown.");
        } catch (SentryAccessDeniedException e) {
            // the excepted exception, recover the version
            sentryStore.verifySentryStoreSchema(false);
        }
    }

    @Test
    public void testRetrieveFullPermssionsImage() throws Exception {

        // Create roles
        String roleName1 = "privs-r1", roleName2 = "privs-r2";
        String groupName1 = "privs-g1";
        String grantor = "g1";
        sentryStore.createSentryRole(roleName1);
        sentryStore.createSentryRole(roleName2);

        // Grant Privileges to the roles
        TSentryPrivilege privilege1 = new TSentryPrivilege();
        privilege1.setPrivilegeScope("TABLE");
        privilege1.setServerName("server1");
        privilege1.setDbName("db1");
        privilege1.setTableName("tbl1");
        privilege1.setAction("SELECT");
        privilege1.setCreateTime(System.currentTimeMillis());
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1, Sets.newHashSet(privilege1),
                null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName2, Sets.newHashSet(privilege1),
                null);

        TSentryPrivilege privilege2 = new TSentryPrivilege();
        privilege2.setPrivilegeScope("SERVER");
        privilege2.setServerName("server1");
        privilege1.setDbName("db2");
        privilege1.setAction("ALL");
        privilege2.setCreateTime(System.currentTimeMillis());
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName2, Sets.newHashSet(privilege2),
                null);

        // Grant roles to the groups
        Set<TSentryGroup> groups = Sets.newHashSet();
        TSentryGroup group = new TSentryGroup();
        group.setGroupName(groupName1);
        groups.add(group);
        sentryStore.alterSentryRoleAddGroups(grantor, roleName1, groups);
        sentryStore.alterSentryRoleAddGroups(grantor, roleName2, groups);

        //Grant owner privilege to role
        TSentryPrivilege privilege3 = new TSentryPrivilege();
        privilege3.setPrivilegeScope("TABLE");
        privilege3.setServerName("server1");
        privilege3.setDbName("db3");
        privilege3.setTableName("tbl1");
        privilege3.setAction("OWNER");
        privilege3.setCreateTime(System.currentTimeMillis());
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1, Sets.newHashSet(privilege3),
                null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName2, Sets.newHashSet(privilege3),
                null);

        PermissionsImage permImage = sentryStore.retrieveFullPermssionsImage();
        Map<String, Map<TPrivilegePrincipal, String>> privs = permImage.getPrivilegeImage();
        Map<String, List<String>> roles = permImage.getRoleImage();
        assertEquals(2, privs.get("db1.tbl1").size());
        assertEquals(2, roles.size());

        assertEquals(2, privs.get("db3.tbl1").size());
        assertEquals("ALL",
                privs.get("db3.tbl1").get(new TPrivilegePrincipal(TPrivilegePrincipalType.ROLE, roleName1)));
        assertEquals("ALL",
                privs.get("db3.tbl1").get(new TPrivilegePrincipal(TPrivilegePrincipalType.ROLE, roleName2)));

    }

    @Test
    public void testRetrieveFullPermssionsImageWithMultiplePrivielgesPerRolePerObject() throws Exception {

        // Create roles
        String roleName1 = "privs-r1";
        String groupName1 = "privs-g1";
        String grantor = "g1";
        sentryStore.createSentryRole(roleName1);

        // Grant roles to the groups
        Set<TSentryGroup> groups = Sets.newHashSet();
        TSentryGroup group = new TSentryGroup();
        group.setGroupName(groupName1);
        groups.add(group);
        sentryStore.alterSentryRoleAddGroups(grantor, roleName1, groups);

        // Grant multiple privileges to a role on one object
        TSentryPrivilege privilege1 = new TSentryPrivilege();
        privilege1.setPrivilegeScope("TABLE");
        privilege1.setServerName("server1");
        privilege1.setDbName("db1");
        privilege1.setTableName("tbl1");
        privilege1.setAction("SELECT");
        privilege1.setCreateTime(System.currentTimeMillis());
        TSentryPrivilege privilege2 = new TSentryPrivilege();
        privilege2.setPrivilegeScope("TABLE");
        privilege2.setServerName("server1");
        privilege2.setDbName("db1");
        privilege2.setTableName("tbl1");
        privilege2.setAction("INSERT");
        privilege2.setCreateTime(System.currentTimeMillis());
        TSentryPrivilege privilege3 = new TSentryPrivilege();
        privilege3.setPrivilegeScope("TABLE");
        privilege3.setServerName("server1");
        privilege3.setDbName("db1");
        privilege3.setTableName("tbl1");
        privilege3.setAction("REFRESH");
        privilege3.setCreateTime(System.currentTimeMillis());
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1, Sets.newHashSet(privilege1),
                null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1, Sets.newHashSet(privilege2),
                null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1, Sets.newHashSet(privilege3),
                null);

        PermissionsImage permImage = sentryStore.retrieveFullPermssionsImage();
        Map<String, Map<TPrivilegePrincipal, String>> privs = permImage.getPrivilegeImage();
        assertEquals(1, privs.get("db1.tbl1").size());
        assertEquals("REFRESH,INSERT,SELECT",
                privs.get("db1.tbl1").get(new TPrivilegePrincipal(TPrivilegePrincipalType.ROLE, roleName1)));

    }

    /**
     * Verifies complete snapshot of HMS Paths can be persisted and retrieved properly.
     */
    @Test
    public void testPersistFullPathsImage() throws Exception {
        Map<String, Collection<String>> authzPaths = new HashMap<>();
        String[] prefixes = { "/user/hive/warehouse" };
        // Makes sure that authorizable object could be associated
        // with different paths and can be properly persisted into database.
        authzPaths.put("db1.table1",
                Sets.newHashSet("/user/hive/warehouse/db2.db/table1.1", "/user/hive/warehouse/db2.db/table1.2"));
        // Makes sure that the same MPaths object could be associated
        // with multiple authorizable and can be properly persisted into database.
        authzPaths.put("db1.table2",
                Sets.newHashSet("/user/hive/warehouse/db2.db/table2.1", "/user/hive/warehouse/db2.db/table2.2"));
        authzPaths.put("db2.table2",
                Sets.newHashSet("/user/hive/warehouse/db2.db/table2.1", "/user/hive/warehouse/db2.db/table2.3"));
        long notificationID = 11;
        sentryStore.persistFullPathsImage(authzPaths, notificationID);
        PathsUpdate pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        long savedNotificationID = sentryStore.getLastProcessedNotificationID();
        assertEquals(1, pathsUpdate.getImgNum());
        TPathsDump pathDump = pathsUpdate.toThrift().getPathsDump();
        Map<Integer, TPathEntry> nodeMap = pathDump.getNodeMap();

        assertEquals(10, nodeMap.size());
        //First element is "/"
        int rootId = pathDump.getRootId();
        TPathEntry root = nodeMap.get(rootId);
        assertEquals("/", root.getPathElement());

        Map<String, Collection<String>> pathImage = new HashMap<>();
        buildPathsImageMap(nodeMap, root, "", pathImage, true);

        assertEquals(3, pathImage.size());
        for (Map.Entry<String, Collection<String>> entry : pathImage.entrySet()) {
            assertEquals(2, entry.getValue().size());
        }
        assertEquals(2, pathImage.get("db2.table2").size());
        assertTrue(CollectionUtils.isEqualCollection(
                Lists.newArrayList("/user/hive/warehouse/db2.db/table1.1", "/user/hive/warehouse/db2.db/table1.2"),
                pathImage.get("db1.table1")));
        assertTrue(CollectionUtils.isEqualCollection(
                Lists.newArrayList("/user/hive/warehouse/db2.db/table2.1", "/user/hive/warehouse/db2.db/table2.2"),
                pathImage.get("db1.table2")));
        assertTrue(CollectionUtils.isEqualCollection(
                Lists.newArrayList("/user/hive/warehouse/db2.db/table2.1", "/user/hive/warehouse/db2.db/table2.3"),
                pathImage.get("db2.table2")));
        assertEquals(6, sentryStore.getPathCount());
        assertEquals(notificationID, savedNotificationID);
    }

    @Test
    public void testAddAuthzPathsMapping() throws Exception {
        Set<MPath> paths;
        // Persist an empty image so that we can add paths to it.
        sentryStore.persistFullPathsImage(new HashMap<String, Collection<String>>(), 0);

        // Check the latest persisted ID matches to both the path updates
        long latestID = sentryStore.getCurrentAuthzPathsSnapshotID();

        // Persist the path
        sentryStore.addAuthzPathsMapping("db1.tb1", Arrays.asList("/hive/db1/tb1"), null);
        paths = sentryStore.getMAuthzPaths(latestID, "db1.tb1");
        // Verify that mapping is been added
        assertEquals(paths.size(), 1);
        assertTrue(paths.stream().anyMatch(x -> x.getPath().equalsIgnoreCase("/hive/db1/tb1")));

        // Add new path to an existing mapping
        sentryStore.addAuthzPathsMapping("db1.tb1", Arrays.asList("/hive/db1/tb1/par1"), null);
        paths = sentryStore.getMAuthzPaths(latestID, "db1.tb1");
        // Verify that mapping is been updated with the new path
        assertEquals(paths.size(), 2);
        assertTrue(paths.stream().anyMatch(x -> x.getPath().equalsIgnoreCase("/hive/db1/tb1/par1")));

        // Add multiples path to an existing mapping
        sentryStore.addAuthzPathsMapping("db1.tb1", Arrays.asList("/hive/db1/tb1/par2", "/hive/db1/tb1/par3"),
                null);
        paths = sentryStore.getMAuthzPaths(latestID, "db1.tb1");
        // Verify that mapping is been updated with the new path
        assertEquals(paths.size(), 4);
        assertTrue(paths.stream().anyMatch(x -> x.getPath().equalsIgnoreCase("/hive/db1/tb1/par2")));
        assertTrue(paths.stream().anyMatch(x -> x.getPath().equalsIgnoreCase("/hive/db1/tb1/par3")));

        //Add null paths to an existing mapping and verify that there are no exceptions
        try {
            sentryStore.addAuthzPathsMapping("db1.tb1", null, null);
        } catch (Exception e) {
            fail("Exception occurred while adding mapping with null paths");
        }

        //Add new mapping with null paths and verify that there are no exceptions
        try {
            sentryStore.addAuthzPathsMapping("db1.tb5", null, null);
        } catch (Exception e) {
            fail("Exception occurred while adding mapping with null paths");
        }

        //Add empty paths collection to existing mapping verify that there are no exceptions
        try {
            sentryStore.addAuthzPathsMapping("db1.tb6", Collections.EMPTY_SET, null);
        } catch (Exception e) {
            fail("Exception occurred while adding mapping with empty path collection");
        }

        //Add new mapping with empty paths collection and verify that there are no exceptions
        try {
            sentryStore.addAuthzPathsMapping("db1.tb1", Collections.EMPTY_SET, null);
        } catch (Exception e) {
            fail("Exception occurred while adding mapping with empty path collection");
        }
    }

    @Test
    public void testAddPathsWithDuplicatedNotificationIdShouldBeAllowed() throws Exception {
        long notificationID = 1;

        // Persist an empty image so that we can add paths to it.
        sentryStore.persistFullPathsImage(new HashMap<String, Collection<String>>(), 0);

        // Create two path updates with the same sequence ID
        UniquePathsUpdate update1 = new UniquePathsUpdate("u1", notificationID, false);
        UniquePathsUpdate update2 = new UniquePathsUpdate("u2", notificationID, false);

        // Populate the path updates with different objects and paths
        update1.newPathChange("db1").addToAddPaths(Arrays.asList("/hive/db1"));
        update2.newPathChange("db2").addToAddPaths(Arrays.asList("/hive/db2"));

        // Persist both path updates. Persisting should be allowed, and paths should be
        // persisted even if they have the same sequence ID
        sentryStore.addAuthzPathsMapping("db1", Arrays.asList("/hive/db1"), update1);
        sentryStore.addAuthzPathsMapping("db2", Arrays.asList("/hive/db2"), update2);

        // Check the latest persisted ID matches to both the path updates
        long latestID = sentryStore.getLastProcessedNotificationID();
        assertEquals(notificationID, latestID);

        String[] prefixes = { "/hive" };
        PathsUpdate pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        TPathsDump pathDump = pathsUpdate.toThrift().getPathsDump();
        Map<Integer, TPathEntry> nodeMap = pathDump.getNodeMap();
        assertEquals(4, nodeMap.size());
        int rootId = pathDump.getRootId();
        TPathEntry root = nodeMap.get(rootId);
        assertEquals("/", root.getPathElement());

        Map<String, Collection<String>> pathsImage = new HashMap<>();
        buildPathsImageMap(nodeMap, root, "", pathsImage, true);

        // Check that retrieving a full paths image returns both paths updates
        assertEquals(2, pathsImage.size());
        assertEquals(1, pathsImage.get("db1").size());
        assertTrue(pathsImage.get("db1").contains("/hive/db1"));
        assertEquals(1, pathsImage.get("db2").size());
        assertTrue(pathsImage.get("db2").contains("/hive/db2"));

        // Check that retrieving delta changes returns both patch updates
        List<MSentryPathChange> pathsChanges = sentryStore.getMSentryPathChanges();
        assertEquals(2, pathsChanges.size());
        assertEquals(1, pathsChanges.get(0).getChangeID()); // changeID = 1
        assertTrue(pathsChanges.get(0).getPathChange().contains("/hive/db1"));
        assertEquals(2, pathsChanges.get(1).getChangeID()); // changeID = 2
        assertTrue(pathsChanges.get(1).getPathChange().contains("/hive/db2"));

        // Check that the SHA1 hash calculated for unique notifications is correct
        assertEquals("u1", pathsChanges.get(0).getNotificationHash());
        assertEquals("u2", pathsChanges.get(1).getNotificationHash());
    }

    private void buildPathsImageMap(Map<Integer, TPathEntry> nodeMap, TPathEntry node, String path,
            Map<String, Collection<String>> pathsImage, boolean includeLeadingSlash) {
        path = path.isEmpty() ? node.getPathElement() : (path + node.getPathElement() + "/");
        TPathEntry tempNode = node;
        for (int childVal : node.getChildren()) {
            node = nodeMap.get(childVal);
            buildPathsImageMap(nodeMap, node, path, pathsImage, includeLeadingSlash);
        }
        if (tempNode.getChildren().isEmpty() && node.getAuthzObjs() != null) {
            for (String authz : node.getAuthzObjs()) {
                if (!pathsImage.containsKey(authz)) {
                    pathsImage.put(authz, new LinkedList<>());
                }
                if (includeLeadingSlash) {
                    pathsImage.get(authz).add(path.replaceAll("(/+$)", ""));
                } else {
                    pathsImage.get(authz).add(path.replaceAll("(^/+)|(/+$)", ""));
                }
            }
        }
    }

    @Test
    public void testPersistDuplicatedNotificationIdShouldBeAllowed() throws Exception {
        long notificationID = 1;

        // Persist the same ID twice should not cause any issues
        sentryStore.persistLastProcessedNotificationID(notificationID);
        sentryStore.persistLastProcessedNotificationID(notificationID);

        // Retrieving latest peristed ID should match with the previous persisted ID
        long latestID = sentryStore.getLastProcessedNotificationID();
        assertEquals(notificationID, latestID);
    }

    @Test
    public void testAddDeleteAuthzPathsMapping() throws Exception {
        long notificationID = 0;

        // Persist an empty image so that we can add paths to it.
        sentryStore.persistFullPathsImage(new HashMap<String, Collection<String>>(), notificationID);

        // Add "db1.table1" authzObj
        Long lastNotificationId = sentryStore.getLastProcessedNotificationID();
        UniquePathsUpdate addUpdate = new UniquePathsUpdate("u1", 1, false);
        addUpdate.newPathChange("db1.table").addToAddPaths(Arrays.asList("db1", "tbl1"));
        addUpdate.newPathChange("db1.table").addToAddPaths(Arrays.asList("db1", "tbl2"));

        sentryStore.addAuthzPathsMapping("db1.table", Sets.newHashSet("db1/tbl1", "db1/tbl2"), addUpdate);
        String[] prefixes = { "/" };
        PathsUpdate pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        TPathsDump pathsDump = pathsUpdate.toThrift().getPathsDump();
        Map<Integer, TPathEntry> nodeMap = pathsDump.getNodeMap();
        TPathEntry root = nodeMap.get(pathsDump.getRootId());
        Map<String, Collection<String>> pathImage = new HashMap<>();
        buildPathsImageMap(nodeMap, root, "", pathImage, false);

        assertEquals(4, nodeMap.size());//Tree size
        assertEquals("/", root.getPathElement());
        assertEquals(1, pathImage.size());
        assertEquals(2, pathImage.get("db1.table").size());
        assertEquals(2, sentryStore.getMPaths().size());

        // Query the persisted path change and ensure it equals to the original one
        long lastChangeID = sentryStore.getLastProcessedPathChangeID();
        MSentryPathChange addPathChange = sentryStore.getMSentryPathChangeByID(lastChangeID);
        assertEquals(addUpdate.JSONSerialize(), addPathChange.getPathChange());
        lastNotificationId = sentryStore.getLastProcessedNotificationID();
        assertEquals(1, lastNotificationId.longValue());

        // Delete path 'db1.db/tbl1' from "db1.table1" authzObj.
        UniquePathsUpdate delUpdate = new UniquePathsUpdate("u2", 2, false);
        delUpdate.newPathChange("db1.table").addToDelPaths(Arrays.asList("db1", "tbl1"));
        sentryStore.deleteAuthzPathsMapping("db1.table", Sets.newHashSet("db1/tbl1"), delUpdate);
        pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        pathsDump = pathsUpdate.toThrift().getPathsDump();
        nodeMap = pathsDump.getNodeMap();
        root = nodeMap.get(pathsDump.getRootId());
        pathImage = new HashMap<>();
        buildPathsImageMap(nodeMap, root, "", pathImage, false);

        assertEquals("/", root.getPathElement());
        assertEquals(3, nodeMap.size());//Tree size
        assertEquals(1, pathImage.size());
        assertEquals(1, pathImage.get("db1.table").size());
        assertEquals(1, sentryStore.getMPaths().size());

        // Query the persisted path change and ensure it equals to the original one
        lastChangeID = sentryStore.getLastProcessedPathChangeID();
        MSentryPathChange delPathChange = sentryStore.getMSentryPathChangeByID(lastChangeID);
        assertEquals(delUpdate.JSONSerialize(), delPathChange.getPathChange());
        lastNotificationId = sentryStore.getLastProcessedNotificationID();
        assertEquals(2, lastNotificationId.longValue());

        // Delete "db1.table" authzObj from the authzObj -> [Paths] mapping.
        UniquePathsUpdate delAllupdate = new UniquePathsUpdate("u3", 3, false);
        delAllupdate.newPathChange("db1.table").addToDelPaths(Lists.newArrayList(PathsUpdate.ALL_PATHS));
        sentryStore.deleteAllAuthzPathsMapping("db1.table", delAllupdate);
        pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        pathsDump = pathsUpdate.toThrift().getPathsDump();
        nodeMap = pathsDump.getNodeMap();
        root = nodeMap.get(pathsDump.getRootId());
        pathImage = new HashMap<>();
        buildPathsImageMap(nodeMap, root, "", pathImage, false);

        assertEquals("/", root.getPathElement());
        assertEquals(1, nodeMap.size());//Tree size
        assertEquals(0, pathImage.size());
        assertEquals(0, sentryStore.getMPaths().size());
        assertEquals(0, sentryStore.getCount(MPath.class).intValue());

        // Query the persisted path change and ensure it equals to the original one
        lastChangeID = sentryStore.getLastProcessedPathChangeID();
        MSentryPathChange delAllPathChange = sentryStore.getMSentryPathChangeByID(lastChangeID);
        assertEquals(delAllupdate.JSONSerialize(), delAllPathChange.getPathChange());

        lastNotificationId = sentryStore.getLastProcessedNotificationID();
        assertEquals(3, lastNotificationId.longValue());

    }

    @Test
    public void testDeleteAuthzPathsMapping() throws Exception {

        long notificationID = 0;

        // Persist an empty image so that we can add paths to it.
        sentryStore.persistFullPathsImage(new HashMap<String, Collection<String>>(), notificationID);

        // Add a mapping to two paths
        sentryStore.addAuthzPathsMapping("db1.table", Sets.newHashSet("db1/tbl1", "db1/tbl2"), null);

        // Verify the Path count
        assertEquals(2, sentryStore.getCount(MPath.class).intValue());

        // Add a mapping to three paths
        sentryStore.addAuthzPathsMapping("db2.table", Sets.newHashSet("db2/tbl1", "db2/tbl2", "db2/tbl3"), null);

        // Verify the Path count
        assertEquals(5, sentryStore.getCount(MPath.class).intValue());

        // deleting the mapping and verifying the MPath count
        sentryStore.deleteAllAuthzPathsMapping("db1.table", null);
        // Verify the Path count
        assertEquals(3, sentryStore.getCount(MPath.class).intValue());

        // deleting the mapping and verifying the MPath count
        sentryStore.deleteAllAuthzPathsMapping("db2.table", null);
        // Verify the Path count
        assertEquals(0, sentryStore.getCount(MPath.class).intValue());

        // Add a mapping to two paths
        sentryStore.addAuthzPathsMapping("db1.table", Sets.newHashSet("db1/tbl1", "db1/tbl2"), null);
        // deleting the mapping with paths as null and verifying that all the paths are deleted.
        sentryStore.deleteAuthzPathsMapping("db1.table", null, null);
        // Verify the Path count
        assertEquals(0, sentryStore.getCount(MPath.class).intValue());

        // Add a mapping to two paths
        sentryStore.addAuthzPathsMapping("db1.table", Sets.newHashSet("db1/tbl1", "db1/tbl2"), null);
        // deleting the mapping with paths as empty set and verifying that all the paths are deleted.
        sentryStore.deleteAuthzPathsMapping("db1.table", Collections.EMPTY_SET, null);
        // Verify the Path count
        assertEquals(0, sentryStore.getCount(MPath.class).intValue());

    }

    @Test
    public void testRenameUpdateAuthzPathsMapping() throws Exception {
        Map<String, Collection<String>> authzPaths = new HashMap<>();
        Long lastNotificationId = sentryStore.getLastProcessedNotificationID();
        authzPaths.put("db1.table1",
                Sets.newHashSet("user/hive/warehouse/db1.db/table1", "user/hive/warehouse/db1.db/table1/p1"));
        authzPaths.put("db1.table2", Sets.newHashSet("user/hive/warehouse/db1.db/table2"));
        sentryStore.persistFullPathsImage(authzPaths, lastNotificationId);
        String[] prefixes = { "/user/hive/warehouse" };
        PathsUpdate pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        TPathsDump pathsDump = pathsUpdate.toThrift().getPathsDump();
        Map<Integer, TPathEntry> nodeMap = pathsDump.getNodeMap();
        TPathEntry root = nodeMap.get(pathsDump.getRootId());
        Map<String, Collection<String>> pathsImage = new HashMap<>();
        buildPathsImageMap(nodeMap, root, "", pathsImage, false);

        assertEquals("/", root.getPathElement());
        assertEquals(8, nodeMap.size());//Tree size
        assertEquals(2, pathsImage.size());

        // Rename path of 'db1.table1' from 'db1.table1' to 'db1.newTable1'
        UniquePathsUpdate renameUpdate = new UniquePathsUpdate("u1", 1, false);
        renameUpdate.newPathChange("db1.table1")
                .addToDelPaths(Arrays.asList("user", "hive", "warehouse", "db1.db", "table1"));
        renameUpdate.newPathChange("db1.newTable1")
                .addToAddPaths(Arrays.asList("user", "hive", "warehouse", "db1.db", "newTable1"));
        sentryStore.renameAuthzPathsMapping("db1.table1", "db1.newTable1", "user/hive/warehouse/db1.db/table1",
                "user/hive/warehouse/db1.db/newTable1", renameUpdate);
        pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        pathsDump = pathsUpdate.toThrift().getPathsDump();
        nodeMap = pathsDump.getNodeMap();
        root = nodeMap.get(pathsDump.getRootId());
        pathsImage = new HashMap<>();
        buildPathsImageMap(nodeMap, root, "", pathsImage, false);

        assertEquals("/", root.getPathElement());
        assertEquals(9, nodeMap.size());//Tree size
        assertEquals(2, pathsImage.size());
        assertEquals(3, sentryStore.getMPaths().size());
        assertTrue(pathsImage.containsKey("db1.newTable1"));
        assertTrue(CollectionUtils.isEqualCollection(
                Lists.newArrayList("user/hive/warehouse/db1.db/table1/p1", "user/hive/warehouse/db1.db/newTable1"),
                pathsImage.get("db1.newTable1")));

        // Query the persisted path change and ensure it equals to the original one
        long lastChangeID = sentryStore.getLastProcessedPathChangeID();
        MSentryPathChange renamePathChange = sentryStore.getMSentryPathChangeByID(lastChangeID);
        assertEquals(renameUpdate.JSONSerialize(), renamePathChange.getPathChange());
        lastNotificationId = sentryStore.getLastProcessedNotificationID();
        assertEquals(1, lastNotificationId.longValue());
        // Rename 'db1.table1' to "db1.table2" but did not change its location.
        renameUpdate = new UniquePathsUpdate("u2", 2, false);
        renameUpdate.newPathChange("db1.newTable1")
                .addToDelPaths(Arrays.asList("user", "hive", "warehouse", "db1.db", "newTable1"));
        renameUpdate.newPathChange("db1.newTable2")
                .addToAddPaths(Arrays.asList("user", "hive", "warehouse", "db1.db", "newTable1"));
        sentryStore.renameAuthzObj("db1.newTable1", "db1.newTable2", renameUpdate);
        pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        pathsDump = pathsUpdate.toThrift().getPathsDump();
        nodeMap = pathsDump.getNodeMap();
        root = nodeMap.get(pathsDump.getRootId());
        pathsImage = new HashMap<>();
        buildPathsImageMap(nodeMap, root, "", pathsImage, false);

        assertEquals("/", root.getPathElement());
        assertEquals(9, nodeMap.size());//Tree size
        assertEquals(2, pathsImage.size());
        assertEquals(3, sentryStore.getMPaths().size());
        assertTrue(pathsImage.containsKey("db1.newTable2"));
        assertTrue(CollectionUtils.isEqualCollection(
                Lists.newArrayList("user/hive/warehouse/db1.db/table1/p1", "user/hive/warehouse/db1.db/newTable1"),
                pathsImage.get("db1.newTable2")));
        lastNotificationId = sentryStore.getLastProcessedNotificationID();
        assertEquals(2, lastNotificationId.longValue());

        // Query the persisted path change and ensure it equals to the original one
        lastChangeID = sentryStore.getLastProcessedPathChangeID();
        renamePathChange = sentryStore.getMSentryPathChangeByID(lastChangeID);
        assertEquals(renameUpdate.JSONSerialize(), renamePathChange.getPathChange());

        // Update path of 'db1.newTable2' from 'db1.newTable1' to 'db1.newTable2'
        UniquePathsUpdate update = new UniquePathsUpdate("u3", 3, false);
        update.newPathChange("db1.newTable1")
                .addToDelPaths(Arrays.asList("user", "hive", "warehouse", "db1.db", "newTable1"));
        update.newPathChange("db1.newTable1")
                .addToAddPaths(Arrays.asList("user", "hive", "warehouse", "db1.db", "newTable2"));
        sentryStore.updateAuthzPathsMapping("db1.newTable2", "user/hive/warehouse/db1.db/newTable1",
                "user/hive/warehouse/db1.db/newTable2", update);
        pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        pathsDump = pathsUpdate.toThrift().getPathsDump();
        nodeMap = pathsDump.getNodeMap();
        root = nodeMap.get(pathsDump.getRootId());
        pathsImage = new HashMap<>();
        buildPathsImageMap(nodeMap, root, "", pathsImage, false);

        assertEquals("/", root.getPathElement());
        assertEquals(9, nodeMap.size());//Tree size
        assertEquals(2, pathsImage.size());
        assertEquals(3, sentryStore.getMPaths().size());
        assertTrue(pathsImage.containsKey("db1.newTable2"));
        assertTrue(CollectionUtils.isEqualCollection(
                Lists.newArrayList("user/hive/warehouse/db1.db/table1/p1", "user/hive/warehouse/db1.db/newTable2"),
                pathsImage.get("db1.newTable2")));

        // Query the persisted path change and ensure it equals to the original one
        lastChangeID = sentryStore.getLastProcessedPathChangeID();
        MSentryPathChange updatePathChange = sentryStore.getMSentryPathChangeByID(lastChangeID);
        assertEquals(update.JSONSerialize(), updatePathChange.getPathChange());
        lastNotificationId = sentryStore.getLastProcessedNotificationID();
        assertEquals(3, lastNotificationId.longValue());
    }

    @Test
    public void testPersistAndReplaceANewPathsImage() throws Exception {
        Map<String, Collection<String>> authzPaths = new HashMap<>();
        long notificationID = 1;

        // First image to persist (this will be replaced later)
        authzPaths.put("db1.table1",
                Sets.newHashSet("/user/hive/warehouse/db2.db/table1.1", "/user/hive/warehouse/db2.db/table1.2"));
        authzPaths.put("db1.table2",
                Sets.newHashSet("/user/hive/warehouse/db2.db/table2.1", "/user/hive/warehouse/db2.db/table2.2"));
        sentryStore.persistFullPathsImage(authzPaths, notificationID);
        String[] prefixes = { "/user/hive/warehouse" };
        PathsUpdate pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        TPathsDump pathsDump = pathsUpdate.toThrift().getPathsDump();
        Map<Integer, TPathEntry> nodeMap = pathsDump.getNodeMap();
        TPathEntry root = nodeMap.get(pathsDump.getRootId());
        Map<String, Collection<String>> pathImage = new HashMap<>();
        buildPathsImageMap(nodeMap, root, "", pathImage, true);

        assertEquals("/", root.getPathElement());
        assertEquals(9, nodeMap.size());//Tree size
        assertEquals(1, pathsUpdate.getImgNum());

        // Second image to persist (it should replace first image)
        authzPaths.clear();
        authzPaths.put("db3.table1",
                Sets.newHashSet("/another-warehouse/db2.db/table1.1", "/another-warehouse/db2.db/table1.2"));
        authzPaths.put("db3.table2",
                Sets.newHashSet("/another-warehouse/db2.db/table2.1", "/another-warehouse/db2.db/table2.2"));
        authzPaths.put("db4.table2",
                Sets.newHashSet("/another-warehouse/db2.db/table2.1", "/another-warehouse/db2.db/table2.3"));
        sentryStore.persistFullPathsImage(authzPaths, notificationID + 1);

        prefixes = new String[] { "/another-warehouse" };
        pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        pathsDump = pathsUpdate.toThrift().getPathsDump();
        nodeMap = pathsDump.getNodeMap();
        root = nodeMap.get(pathsDump.getRootId());
        pathImage = new HashMap<>();
        buildPathsImageMap(nodeMap, root, "", pathImage, true);

        assertEquals("/", root.getPathElement());
        assertEquals(8, nodeMap.size());//Tree size
        assertEquals(2, pathsUpdate.getImgNum());
        assertEquals(3, pathImage.size());

        for (Map.Entry<String, Collection<String>> entry : pathImage.entrySet()) {
            assertEquals(2, entry.getValue().size());
        }

        assertEquals(2, pathImage.get("db4.table2").size());
        assertTrue(CollectionUtils.isEqualCollection(
                Lists.newArrayList("/another-warehouse/db2.db/table1.1", "/another-warehouse/db2.db/table1.2"),
                pathImage.get("db3.table1")));
        assertTrue(CollectionUtils.isEqualCollection(
                Lists.newArrayList("/another-warehouse/db2.db/table2.1", "/another-warehouse/db2.db/table2.2"),
                pathImage.get("db3.table2")));

        assertTrue(CollectionUtils.isEqualCollection(
                Lists.newArrayList("/another-warehouse/db2.db/table2.1", "/another-warehouse/db2.db/table2.3"),
                pathImage.get("db4.table2")));

        assertEquals(6, sentryStore.getMPaths().size());
    }

    @Test
    public void testAddDeleteAfterReplacingANewPathsImage() throws Exception {
        long notificationID = 1;

        // Add some paths first (these should be replaced)
        UniquePathsUpdate addUpdate = new UniquePathsUpdate("u1", notificationID, false);
        addUpdate.newPathChange("db1.table").addToAddPaths(Arrays.asList("db1", "tbl1"));
        addUpdate.newPathChange("db1.table").addToAddPaths(Arrays.asList("db1", "tbl2"));
        sentryStore.addAuthzPathsMapping("db1.table", Sets.newHashSet("db1/tbl1", "db1/tbl2"), addUpdate);

        // Persist a new image that contains a new image ID (it replaces previous paths)
        notificationID++;
        Map<String, Collection<String>> authzPaths = new HashMap<>();
        authzPaths.put("db2.table3",
                Sets.newHashSet("/user/hive/warehouse/db2.db/table1.1", "/user/hive/warehouse/db2.db/table1.2"));
        sentryStore.persistFullPathsImage(authzPaths, notificationID);

        // Add new paths
        notificationID++;
        UniquePathsUpdate newAddUpdate = new UniquePathsUpdate("u2", notificationID, false);
        newAddUpdate.newPathChange("db2.table").addToAddPaths(Arrays.asList("db2", "tbl1"));
        newAddUpdate.newPathChange("db2.table").addToAddPaths(Arrays.asList("db2", "tbl2"));
        sentryStore.addAuthzPathsMapping("db2.table", Sets.newHashSet("db2/tbl1", "db2/tbl2"), newAddUpdate);
        String[] prefixes = { "/" };
        PathsUpdate pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        TPathsDump pathsDump = pathsUpdate.toThrift().getPathsDump();
        Map<Integer, TPathEntry> nodeMap = pathsDump.getNodeMap();
        TPathEntry root = nodeMap.get(pathsDump.getRootId());
        Map<String, Collection<String>> pathImage = new HashMap<>();
        buildPathsImageMap(nodeMap, root, "", pathImage, false);

        assertEquals("/", root.getPathElement());
        assertEquals(10, nodeMap.size());//Tree size
        assertEquals(2, pathImage.size());
        assertEquals(2, pathImage.get("db2.table").size());
        assertEquals(4, sentryStore.getMPaths().size());

        // Delete one path
        notificationID++;
        UniquePathsUpdate delUpdate = new UniquePathsUpdate("u3", notificationID, false);
        delUpdate.newPathChange("db2.table").addToDelPaths(Arrays.asList("db2", "tbl1"));
        sentryStore.deleteAuthzPathsMapping("db2.table", Sets.newHashSet("db2/tbl1"), delUpdate);
        pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        pathsDump = pathsUpdate.toThrift().getPathsDump();
        nodeMap = pathsDump.getNodeMap();
        root = nodeMap.get(pathsDump.getRootId());
        pathImage = new HashMap<>();
        buildPathsImageMap(nodeMap, root, "", pathImage, false);

        assertEquals("/", root.getPathElement());
        assertEquals(9, nodeMap.size());//Tree size
        assertEquals(2, pathImage.size());
        assertEquals(1, pathImage.get("db2.table").size());
        assertEquals(3, sentryStore.getMPaths().size());

        Long lastNotificationId = sentryStore.getLastProcessedNotificationID();
        assertEquals(notificationID, lastNotificationId.longValue());
    }

    @Test
    public void testRenameUpdateAfterReplacingANewPathsImage() throws Exception {
        long notificationID = 1;

        Map<String, Collection<String>> authzPaths = new HashMap<>();
        // First image to persist (this will be replaced later)
        authzPaths.put("db1.table1",
                Sets.newHashSet("/user/hive/warehouse/db2.db/table1.1", "/user/hive/warehouse/db2.db/table1.2"));
        authzPaths.put("db1.table2",
                Sets.newHashSet("/user/hive/warehouse/db2.db/table2.1", "/user/hive/warehouse/db2.db/table2.2"));
        sentryStore.persistFullPathsImage(authzPaths, notificationID);

        // Second image to persist (it should replace first image)
        notificationID++;
        authzPaths.clear();
        authzPaths.put("db3.table1",
                Sets.newHashSet("/another-warehouse/db3.db/table1.1", "/another-warehouse/db3.db/table1.2"));
        authzPaths.put("db3.table2",
                Sets.newHashSet("/another-warehouse/db3.db/table2.1", "/another-warehouse/db3.db/table2.2"));
        sentryStore.persistFullPathsImage(authzPaths, notificationID);

        // Rename path of 'db1.table1' from 'db1.table1' to 'db1.newTable1'
        notificationID++;
        UniquePathsUpdate renameUpdate = new UniquePathsUpdate("u1", notificationID, false);
        renameUpdate.newPathChange("db3.table1")
                .addToDelPaths(Arrays.asList("another-warehouse", "db3.db", "table1.1"));
        renameUpdate.newPathChange("db1.newTable1")
                .addToAddPaths(Arrays.asList("user", "hive", "warehouse", "db1.db", "newTable1"));
        sentryStore.renameAuthzPathsMapping("db3.table1", "db1.newTable1", "/another-warehouse/db3.db/table1.1",
                "user/hive/warehouse/db1.db/newTable1", renameUpdate);
        String[] prefixes = { "/" };
        PathsUpdate pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        TPathsDump pathsDump = pathsUpdate.toThrift().getPathsDump();
        Map<Integer, TPathEntry> nodeMap = pathsDump.getNodeMap();
        TPathEntry root = nodeMap.get(pathsDump.getRootId());
        Map<String, Collection<String>> pathsImage = new HashMap<>();
        buildPathsImageMap(nodeMap, root, "", pathsImage, true);

        assertEquals("/", root.getPathElement());
        assertEquals(11, nodeMap.size());//Tree size
        assertEquals(2, pathsImage.size());
        assertEquals(4, sentryStore.getMPaths().size());
        assertTrue(pathsImage.containsKey("db1.newTable1"));
        assertTrue(CollectionUtils.isEqualCollection(
                Lists.newArrayList("/another-warehouse/db3.db/table1.2", "/user/hive/warehouse/db1.db/newTable1"),
                pathsImage.get("db1.newTable1")));

        // Update path of 'db1.newTable2' from 'db1.newTable1' to 'db1.newTable2'
        notificationID++;
        UniquePathsUpdate update = new UniquePathsUpdate("u2", notificationID, false);
        update.newPathChange("db1.newTable1")
                .addToDelPaths(Arrays.asList("user", "hive", "warehouse", "db1.db", "newTable1"));
        update.newPathChange("db1.newTable1")
                .addToAddPaths(Arrays.asList("user", "hive", "warehouse", "db1.db", "newTable2"));
        sentryStore.updateAuthzPathsMapping("db1.newTable2", "user/hive/warehouse/db1.db/newTable1",
                "user/hive/warehouse/db1.db/newTable2", update);
        pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        pathsDump = pathsUpdate.toThrift().getPathsDump();
        nodeMap = pathsDump.getNodeMap();
        root = nodeMap.get(pathsDump.getRootId());
        pathsImage = new HashMap<>();
        buildPathsImageMap(nodeMap, root, "", pathsImage, false);

        assertEquals("/", root.getPathElement());
        assertEquals(12, nodeMap.size());//Tree size
        assertEquals(3, pathsImage.size());
        assertEquals(5, sentryStore.getMPaths().size());
        assertTrue(pathsImage.containsKey("db1.newTable2"));
        assertEquals(Lists.newArrayList("user/hive/warehouse/db1.db/newTable2"), pathsImage.get("db1.newTable2"));
    }

    @Test
    public void testQueryParamBuilder() {
        QueryParamBuilder paramBuilder;
        paramBuilder = newQueryParamBuilder();
        // Try single parameter
        paramBuilder.add("key", "val");
        assertEquals("(this.key == :key)", paramBuilder.toString());
        // Try adding second parameter plus add trimming and conversion
        // to lower case
        paramBuilder.add("key1", " Val1 ", true);
        assertEquals("(this.key == :key && this.key1 == :key1)", paramBuilder.toString());
        Map<String, Object> params = paramBuilder.getArguments();
        assertEquals("val", params.get("key"));
        assertEquals("Val1", params.get("key1"));

        paramBuilder = newQueryParamBuilder(QueryParamBuilder.Op.OR);
        paramBuilder.add("key", " Val ", true);
        paramBuilder.addNotNull("notNullField");
        paramBuilder.addNull("nullField");
        assertEquals("(this.key == :key || this.notNullField != \"__NULL__\" || this.nullField == \"__NULL__\")",
                paramBuilder.toString());
        params = paramBuilder.getArguments();
        assertEquals("Val", params.get("key"));

        paramBuilder = newQueryParamBuilder().addNull("var1").addNotNull("var2");
        assertEquals("(this.var1 == \"__NULL__\" && this.var2 != \"__NULL__\")", paramBuilder.toString());

        // Test newChild()
        paramBuilder = newQueryParamBuilder();
        paramBuilder.addString("e1").addString("e2").newChild().add("v3", "e3").add("v4", "e4").newChild()
                .addString("e5").addString("e6");
        assertEquals("(e1 && e2 && (this.v3 == :v3 || this.v4 == :v4 || (e5 && e6)))", paramBuilder.toString());

        paramBuilder = newQueryParamBuilder();
        paramBuilder.addString("e1").addString("e2").newChild().add("v3", "e3").add("v4", "e4").newChild()
                .addString("e5").addString("e6");

        params = paramBuilder.getArguments();
        assertEquals("e3", params.get("v3"));
        assertEquals("e4", params.get("v4"));

        // Test addSet
        paramBuilder = newQueryParamBuilder();
        Set<String> names = new HashSet<>();
        names.add("foo");
        names.add("bar");
        names.add("bob");

        paramBuilder.addSet("prefix == ", names, true);
        assertEquals("(prefix == :var0 && prefix == :var1 && prefix == :var2)", paramBuilder.toString());
        params = paramBuilder.getArguments();

        Set<String> result = new HashSet<>();
        result.add((String) params.get("var0"));
        result.add((String) params.get("var1"));
        result.add((String) params.get("var2"));
        assertTrue(result.containsAll(names));
        assertTrue(names.containsAll(result));
    }

    @Test
    public void testPrivilegesWithPermUpdate() throws Exception {
        String roleName = "test-privilege";
        String server = "server1";
        String db = "db1";
        String table = "tbl1";
        String authzObj = "db1.tbl1";
        createRole(roleName);

        TSentryPrivilege privilege = new TSentryPrivilege();
        privilege.setPrivilegeScope("Column");
        privilege.setServerName(server);
        privilege.setDbName(db);
        privilege.setTableName(table);
        privilege.setAction(AccessConstants.SELECT);
        privilege.setCreateTime(System.currentTimeMillis());

        // Generate the permission add update authzObj "db1.tbl1"
        PermissionsUpdate addUpdate = new PermissionsUpdate(0, false);
        addUpdate.addPrivilegeUpdate(authzObj).putToAddPrivileges(
                new TPrivilegePrincipal(TPrivilegePrincipalType.ROLE, roleName),
                privilege.getAction().toUpperCase());

        // Grant the privilege to role test-privilege and verify it has been persisted.
        Map<TSentryPrivilege, Updateable.Update> addPrivilegesUpdateMap = Maps.newHashMap();
        addPrivilegesUpdateMap.put(privilege, addUpdate);
        sentryStore.alterSentryRoleGrantPrivileges(roleName, Sets.newHashSet(privilege), addPrivilegesUpdateMap);
        MSentryRole role = sentryStore.getMSentryRoleByName(roleName);
        Set<MSentryPrivilege> privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 1, privileges.size());

        // Query the persisted perm change and ensure it equals to the original one
        long lastChangeID = sentryStore.getLastProcessedPermChangeID();
        long initialID = lastChangeID;
        MSentryPermChange addPermChange = sentryStore.getMSentryPermChangeByID(lastChangeID);
        assertEquals(addUpdate.JSONSerialize(), addPermChange.getPermChange());

        // Generate the permission delete update authzObj "db1.tbl1"
        PermissionsUpdate delUpdate = new PermissionsUpdate(0, false);
        delUpdate.addPrivilegeUpdate(authzObj).putToDelPrivileges(
                new TPrivilegePrincipal(TPrivilegePrincipalType.ROLE, roleName),
                privilege.getAction().toUpperCase());

        // Revoke the same privilege and verify it has been removed.
        Map<TSentryPrivilege, Updateable.Update> delPrivilegesUpdateMap = Maps.newHashMap();
        delPrivilegesUpdateMap.put(privilege, delUpdate);
        sentryStore.alterSentryRoleRevokePrivileges(roleName, Sets.newHashSet(privilege), delPrivilegesUpdateMap);
        role = sentryStore.getMSentryRoleByName(roleName);
        privileges = role.getPrivileges();
        assertEquals(0, privileges.size());

        // Query the persisted perm change and ensure it equals to the original one
        lastChangeID = sentryStore.getLastProcessedPermChangeID();
        MSentryPermChange delPermChange = sentryStore.getMSentryPermChangeByID(lastChangeID);
        assertEquals(delUpdate.JSONSerialize(), delPermChange.getPermChange());

        // Verify getMSentryPermChanges will return all MSentryPermChanges up
        // to the given changeID.
        List<MSentryPermChange> mSentryPermChanges = sentryStore.getMSentryPermChanges(initialID);
        assertEquals(lastChangeID - initialID + 1, mSentryPermChanges.size());

        // Verify ifPermChangeExists will return true for persisted MSentryPermChange.
        assertEquals(true, sentryStore.permChangeExists(1));
    }

    @Test
    public void testAddDeleteGroupsWithPermUpdate() throws Exception {
        String roleName = "test-groups";
        String grantor = "g1";
        createRole(roleName);

        Set<TSentryGroup> groups = Sets.newHashSet();
        TSentryGroup group = new TSentryGroup();
        group.setGroupName("test-groups-g1");
        groups.add(group);
        group = new TSentryGroup();
        group.setGroupName("test-groups-g2");
        groups.add(group);

        // Generate the permission add update for role "test-groups"
        PermissionsUpdate addUpdate = new PermissionsUpdate(0, false);
        TRoleChanges addrUpdate = addUpdate.addRoleUpdate(roleName);
        for (TSentryGroup g : groups) {
            addrUpdate.addToAddGroups(g.getGroupName());
        }

        // Assign the role "test-groups" to the groups and verify.
        sentryStore.alterSentryRoleAddGroups(grantor, roleName, groups, addUpdate);
        MSentryRole role = sentryStore.getMSentryRoleByName(roleName);
        assertEquals(2, role.getGroups().size());

        // Query the persisted perm change and ensure it equals to the original one
        long lastChangeID = sentryStore.getLastProcessedPermChangeID();
        MSentryPermChange addPermChange = sentryStore.getMSentryPermChangeByID(lastChangeID);
        assertEquals(addUpdate.JSONSerialize(), addPermChange.getPermChange());

        // Generate the permission add update for role "test-groups"
        PermissionsUpdate delUpdate = new PermissionsUpdate(0, false);
        TRoleChanges delrUpdate = delUpdate.addRoleUpdate(roleName);
        for (TSentryGroup g : groups) {
            delrUpdate.addToDelGroups(g.getGroupName());
        }

        // Revoke the role "test-groups" to the groups and verify.
        sentryStore.alterSentryRoleDeleteGroups(roleName, groups, delUpdate);
        role = sentryStore.getMSentryRoleByName(roleName);
        assertEquals(Collections.emptySet(), role.getGroups());

        // Query the persisted perm change and ensure it equals to the original one
        MSentryPermChange delPermChange = sentryStore.getMSentryPermChangeByID(lastChangeID + 1);
        assertEquals(delUpdate.JSONSerialize(), delPermChange.getPermChange());
    }

    @Test
    public void testCreateDropRoleWithPermUpdate() throws Exception {
        String roleName = "test-drop-role";
        createRole(roleName);

        // Generate the permission del update for dropping role "test-drop-role"
        PermissionsUpdate delUpdate = new PermissionsUpdate(0, false);
        delUpdate.addPrivilegeUpdate(PermissionsUpdate.ALL_AUTHZ_OBJ).putToDelPrivileges(
                new TPrivilegePrincipal(TPrivilegePrincipalType.ROLE, roleName), PermissionsUpdate.ALL_AUTHZ_OBJ);
        delUpdate.addRoleUpdate(roleName).addToDelGroups(PermissionsUpdate.ALL_GROUPS);

        // Drop the role and verify.
        sentryStore.dropSentryRole(roleName, delUpdate);
        checkRoleDoesNotExist(roleName);

        // Query the persisted perm change and ensure it equals to the original one
        long lastChangeID = sentryStore.getLastProcessedPermChangeID();
        MSentryPermChange delPermChange = sentryStore.getMSentryPermChangeByID(lastChangeID);
        assertEquals(delUpdate.JSONSerialize(), delPermChange.getPermChange());
    }

    @Test
    public void testDropObjWithPermUpdate() throws Exception {
        String roleName1 = "list-privs-r1", roleName2 = "list-privs-r2";
        sentryStore.createSentryRole(roleName1);
        sentryStore.createSentryRole(roleName2);

        String authzObj = "db1.tbl1";
        TSentryPrivilege privilege_tbl1 = new TSentryPrivilege();
        privilege_tbl1.setPrivilegeScope("TABLE");
        privilege_tbl1.setServerName("server1");
        privilege_tbl1.setDbName("db1");
        privilege_tbl1.setTableName("tbl1");
        privilege_tbl1.setCreateTime(System.currentTimeMillis());
        privilege_tbl1.setAction("SELECT");

        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1, Sets.newHashSet(privilege_tbl1),
                null);

        // Generate the permission drop update for dropping privilege for "db1.tbl1"
        PermissionsUpdate dropUpdate = new PermissionsUpdate(0, false);
        dropUpdate.addPrivilegeUpdate(authzObj).putToDelPrivileges(
                new TPrivilegePrincipal(TPrivilegePrincipalType.ROLE, PermissionsUpdate.ALL_ROLES),
                PermissionsUpdate.ALL_ROLES);

        // Drop the privilege and verify.
        sentryStore.dropPrivilege(toTSentryAuthorizable(privilege_tbl1), dropUpdate);
        assertEquals(0, sentryStore.getAllTSentryPrivilegesByRoleName(roleName1).size());
        assertEquals(0, sentryStore.getAllTSentryPrivilegesByRoleName(roleName2).size());

        // Query the persisted perm change and ensure it equals to the original one
        long lastChangeID = sentryStore.getLastProcessedPermChangeID();
        MSentryPermChange dropPermChange = sentryStore.getMSentryPermChangeByID(lastChangeID);
        assertEquals(dropUpdate.JSONSerialize(), dropPermChange.getPermChange());
    }

    @Test
    public void testRenameObjWithPermUpdate() throws Exception {
        String roleName1 = "role1";
        String table1 = "tbl1", table2 = "tbl2";

        sentryStore.createSentryRole(roleName1);

        TSentryPrivilege privilege_tbl1 = new TSentryPrivilege();
        privilege_tbl1.setPrivilegeScope("TABLE");
        privilege_tbl1.setServerName("server1");
        privilege_tbl1.setDbName("db1");
        privilege_tbl1.setTableName(table1);
        privilege_tbl1.setCreateTime(System.currentTimeMillis());
        privilege_tbl1.setAction(AccessConstants.ALL);

        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1, Sets.newHashSet(privilege_tbl1),
                null);

        // Generate the permission rename update for renaming privilege for "db1.tbl1"
        String oldAuthz = "db1.tbl1";
        String newAuthz = "db1.tbl2";
        PermissionsUpdate renameUpdate = new PermissionsUpdate(0, false);
        TPrivilegeChanges privUpdate = renameUpdate.addPrivilegeUpdate(PermissionsUpdate.RENAME_PRIVS);
        privUpdate.putToAddPrivileges(new TPrivilegePrincipal(TPrivilegePrincipalType.AUTHZ_OBJ, newAuthz),
                newAuthz);
        privUpdate.putToDelPrivileges(new TPrivilegePrincipal(TPrivilegePrincipalType.AUTHZ_OBJ, oldAuthz),
                oldAuthz);

        // Rename the privilege and verify.
        TSentryAuthorizable oldTable = toTSentryAuthorizable(privilege_tbl1);
        TSentryAuthorizable newTable = toTSentryAuthorizable(privilege_tbl1);
        newTable.setTable(table2);
        sentryStore.renamePrivilege(oldTable, newTable, renameUpdate);

        Set<TSentryPrivilege> privilegeSet = sentryStore.getAllTSentryPrivilegesByRoleName(roleName1);
        assertEquals(1, privilegeSet.size());
        for (TSentryPrivilege privilege : privilegeSet) {
            assertTrue(table2.equalsIgnoreCase(privilege.getTableName()));
        }

        // Query the persisted perm change and ensure it equals to the original one
        long lastChangeID = sentryStore.getLastProcessedPermChangeID();
        MSentryPermChange renamePermChange = sentryStore.getMSentryPermChangeByID(lastChangeID);
        assertEquals(renameUpdate.JSONSerialize(), renamePermChange.getPermChange());
    }

    protected static void addGroupsToUser(String user, String... groupNames) {
        policyFile.addGroupsToUser(user, groupNames);
    }

    protected static void writePolicyFile() throws Exception {
        policyFile.write(policyFilePath);
    }

    @Test
    public void testPurgeDeltaChanges() throws Exception {
        String role = "purgeRole";
        String table = "purgeTable";

        assertEquals(0, sentryStore.getMSentryPermChanges().size());
        assertEquals(0, sentryStore.getMSentryPathChanges().size());

        sentryStore.createSentryRole(role);
        int privCleanCount = ServerConfig.SENTRY_DELTA_KEEP_COUNT_DEFAULT;
        int extraPrivs = 5;

        final int numPermChanges = extraPrivs + privCleanCount;
        for (int i = 0; i < numPermChanges; i++) {
            TSentryPrivilege privilege = new TSentryPrivilege();
            privilege.setPrivilegeScope("Column");
            privilege.setServerName("server");
            privilege.setDbName("db");
            privilege.setTableName(table);
            privilege.setColumnName("column");
            privilege.setAction(AccessConstants.SELECT);
            privilege.setCreateTime(System.currentTimeMillis());

            PermissionsUpdate update = new PermissionsUpdate(i + 1, false);
            sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, role, Sets.newHashSet(privilege),
                    Lists.newArrayList(update));
        }
        assertEquals(numPermChanges, sentryStore.getMSentryPermChanges().size());

        sentryStore.purgeDeltaChangeTables();
        assertEquals(privCleanCount, sentryStore.getMSentryPermChanges().size());

        // TODO: verify MSentryPathChange being purged.
        // assertEquals(1, sentryStore.getMSentryPathChanges().size());
    }

    @Test
    public void testpurgeNotificationIdTable() throws Exception {

        int totalentires = 200;
        int remainingEntires = ServerConfig.SENTRY_HMS_NOTIFICATION_ID_KEEP_COUNT_DEFAULT;
        assertTrue(sentryStore.isHmsNotificationEmpty());
        for (int id = 1; id <= totalentires; id++) {
            sentryStore.persistLastProcessedNotificationID((long) id);
        }
        assertEquals(totalentires, sentryStore.getMSentryHmsNotificationCore().size());
        sentryStore.purgeNotificationIdTable();

        // Make sure that sentry store still hold entries based on SENTRY_HMS_NOTIFICATION_ID_KEEP_COUNT_DEFAULT
        assertEquals(remainingEntires, sentryStore.getMSentryHmsNotificationCore().size());

        sentryStore.purgeNotificationIdTable();
        // Make sure that sentry store still hold entries based on SENTRY_HMS_NOTIFICATION_ID_KEEP_COUNT_DEFAULT
        assertEquals(remainingEntires, sentryStore.getMSentryHmsNotificationCore().size());

        sentryStore.clearAllTables();

        totalentires = 50;
        for (int id = 1; id <= totalentires; id++) {
            sentryStore.persistLastProcessedNotificationID((long) id);
        }
        assertEquals(totalentires, sentryStore.getMSentryHmsNotificationCore().size());
        sentryStore.purgeNotificationIdTable();

        // Make sure that sentry store still holds all the entries as total entries is less than
        // SENTRY_HMS_NOTIFICATION_ID_KEEP_COUNT_DEFAULT.
        assertEquals(totalentires, sentryStore.getMSentryHmsNotificationCore().size());
    }

    /**
     * This test verifies that in the case of concurrently updating delta change tables, no gap
     * between change ID was made. All the change IDs must be consecutive ({@see SENTRY-1643}).
     *
     * @throws Exception
     */
    @Test(timeout = 60000)
    public void testConcurrentUpdateChanges() throws Exception {
        final int numThreads = 20;
        final int numChangesPerThread = 100;
        final TransactionManager tm = sentryStore.getTransactionManager();
        final AtomicLong seqNumGenerator = new AtomicLong(0);
        final CyclicBarrier barrier = new CyclicBarrier(numThreads);

        ExecutorService executor = Executors.newFixedThreadPool(numThreads);
        for (int i = 0; i < numThreads; i++) {
            executor.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        barrier.await();
                    } catch (Exception e) {
                        LOGGER.error("Barrier failed to await", e);
                        return;
                    }
                    for (int j = 0; j < numChangesPerThread; j++) {
                        List<TransactionBlock<Object>> tbs = new ArrayList<>();
                        PermissionsUpdate update = new PermissionsUpdate(seqNumGenerator.getAndIncrement(), false);
                        tbs.add(new DeltaTransactionBlock(update));
                        try {
                            tm.executeTransactionBlocksWithRetry(tbs);
                        } catch (Exception e) {
                            LOGGER.error("Failed to execute permission update transaction", e);
                            fail(String.format("Transaction failed: %s", e.getMessage()));
                        }
                    }
                }
            });
        }
        executor.shutdown();
        executor.awaitTermination(60, TimeUnit.SECONDS);

        List<MSentryPermChange> changes = sentryStore.getMSentryPermChanges();
        int actualSize = changes.size();
        if (actualSize != (numThreads * numChangesPerThread)) {
            LOGGER.warn("Detected {} dropped changes", ((numChangesPerThread * numThreads) - actualSize));
        }
        TreeSet<Long> changeIDs = new TreeSet<>();
        for (MSentryPermChange change : changes) {
            changeIDs.add(change.getChangeID());
        }
        assertEquals("duplicated change ID", actualSize, changeIDs.size());

        long prevId = changeIDs.first() - 1;
        for (Long changeId : changeIDs) {
            assertTrue(String.format("Found non-consecutive number: prev=%d cur=%d", prevId, changeId),
                    changeId - prevId == 1);
            prevId = changeId;
        }
    }

    @Test
    public void testDuplicateNotification() throws Exception {
        Map<String, Collection<String>> authzPaths = new HashMap<>();
        Long lastNotificationId = sentryStore.getLastProcessedNotificationID();

        lastNotificationId++;
        authzPaths.put("db1.table1",
                Sets.newHashSet("user/hive/warehouse/db1.db/table1", "user/hive/warehouse/db1.db/table1/p1"));
        authzPaths.put("db1.table2", Sets.newHashSet("user/hive/warehouse/db1.db/table2"));
        sentryStore.persistFullPathsImage(authzPaths, lastNotificationId);
        String[] prefixes = { "/user/hive/warehouse" };
        PathsUpdate pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        TPathsDump pathsDump = pathsUpdate.toThrift().getPathsDump();
        Map<Integer, TPathEntry> nodeMap = pathsDump.getNodeMap();
        TPathEntry root = nodeMap.get(pathsDump.getRootId());
        Map<String, Collection<String>> pathsImage = new HashMap<>();
        buildPathsImageMap(nodeMap, root, "", pathsImage, false);

        assertEquals("/", root.getPathElement());
        assertEquals(8, nodeMap.size());//Tree size
        assertEquals(2, pathsImage.size());

        if (lastNotificationId == null) {
            lastNotificationId = SentryConstants.EMPTY_NOTIFICATION_ID;
        }

        // Rename path of 'db1.table1' from 'db1.table1' to 'db1.newTable1'
        lastNotificationId++;
        UniquePathsUpdate renameUpdate = new UniquePathsUpdate("u1", lastNotificationId, false);
        renameUpdate.newPathChange("db1.table1")
                .addToDelPaths(Arrays.asList("user", "hive", "warehouse", "db1.db", "table1"));
        renameUpdate.newPathChange("db1.newTable1")
                .addToAddPaths(Arrays.asList("user", "hive", "warehouse", "db1.db", "newTable1"));
        sentryStore.renameAuthzPathsMapping("db1.table1", "db1.newTable1", "user/hive/warehouse/db1.db/table1",
                "user/hive/warehouse/db1.db/newTable1", renameUpdate);
        pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        pathsDump = pathsUpdate.toThrift().getPathsDump();
        nodeMap = pathsDump.getNodeMap();
        root = nodeMap.get(pathsDump.getRootId());
        pathsImage = new HashMap<>();
        buildPathsImageMap(nodeMap, root, "", pathsImage, false);

        assertEquals("/", root.getPathElement());
        assertEquals(9, nodeMap.size());//Tree size
        assertEquals(2, pathsImage.size());
        assertEquals(3, sentryStore.getMPaths().size());
        assertTrue(pathsImage.containsKey("db1.newTable1"));
        assertTrue(CollectionUtils.isEqualCollection(
                Lists.newArrayList("user/hive/warehouse/db1.db/table1/p1", "user/hive/warehouse/db1.db/newTable1"),
                pathsImage.get("db1.newTable1")));

        // Query the persisted path change and ensure it equals to the original one
        long lastChangeID = sentryStore.getLastProcessedPathChangeID();
        MSentryPathChange renamePathChange = sentryStore.getMSentryPathChangeByID(lastChangeID);
        assertEquals(renameUpdate.JSONSerialize(), renamePathChange.getPathChange());
        Long savedLastNotificationId = sentryStore.getLastProcessedNotificationID();
        assertEquals(lastNotificationId.longValue(), savedLastNotificationId.longValue());

        // Process the notificaiton second time
        try {
            sentryStore.renameAuthzPathsMapping("db1.table1", "db1.newTable1", "user/hive/warehouse/db1.db/table1",
                    "user/hive/warehouse/db1.db/newTable1", renameUpdate);
        } catch (Exception e) {
            if (!(e.getCause() instanceof JDODataStoreException)) {
                fail("Unexpected failure occured while processing duplicate notification");
            }
        }
    }

    @Test
    public void testIsAuthzPathsMappingEmpty() throws Exception {
        // Add "db1.table1" authzObj
        UniquePathsUpdate addUpdate = new UniquePathsUpdate("u1", 1, false);
        addUpdate.newPathChange("db1.table").addToAddPaths(Arrays.asList("db1", "tbl1"));
        addUpdate.newPathChange("db1.table").addToAddPaths(Arrays.asList("db1", "tbl2"));

        // Persist an empty image so that we can add paths to it.
        sentryStore.persistFullPathsImage(new HashMap<String, Collection<String>>(), 0);

        assertEquals(sentryStore.isAuthzPathsMappingEmpty(), true);
        sentryStore.addAuthzPathsMapping("db1.table", Sets.newHashSet("db1/tbl1", "db1/tbl2"), addUpdate);
        String[] prefixes = { "/" };
        PathsUpdate pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        TPathsDump pathsDump = pathsUpdate.toThrift().getPathsDump();
        Map<Integer, TPathEntry> nodeMap = pathsDump.getNodeMap();
        TPathEntry root = nodeMap.get(pathsDump.getRootId());
        Map<String, Collection<String>> pathImage = new HashMap<>();
        buildPathsImageMap(nodeMap, root, "", pathImage, false);

        assertEquals("/", root.getPathElement());
        assertEquals(4, nodeMap.size());//Tree size
        assertEquals(1, pathImage.size());
        assertEquals(2, pathImage.get("db1.table").size());
        assertEquals(2, sentryStore.getMPaths().size());
        assertEquals(sentryStore.isAuthzPathsMappingEmpty(), false);
        sentryStore.clearAllTables();
        assertEquals(sentryStore.isAuthzPathsMappingEmpty(), true);
    }

    @Test
    public void testAddDeleteAuthzPathsMappingNoDeltaSavedWithoutHDFSSync() throws Exception {

        // disable HDFS
        conf.set(ServiceConstants.ServerConfig.PROCESSOR_FACTORIES, "");
        conf.set(ServiceConstants.ServerConfig.SENTRY_POLICY_STORE_PLUGINS, "");
        SentryStore localSentryStore = new SentryStore(conf);

        // Persist an empty image so that we can add paths to it.
        localSentryStore.persistFullPathsImage(new HashMap<String, Collection<String>>(), 0);

        // Add "db1.table1" authzObj
        Long lastNotificationId = sentryStore.getLastProcessedNotificationID();
        UniquePathsUpdate addUpdate = new UniquePathsUpdate("u1", 1, false);
        addUpdate.newPathChange("db1.table").addToAddPaths(Arrays.asList("db1", "tbl1"));
        addUpdate.newPathChange("db1.table").addToAddPaths(Arrays.asList("db1", "tbl2"));

        localSentryStore.addAuthzPathsMapping("db1.table", Sets.newHashSet("db1/tbl1", "db1/tbl2"), addUpdate);
        String[] prefixes = { "/" };
        PathsUpdate pathsUpdate = localSentryStore.retrieveFullPathsImageUpdate(prefixes);
        TPathsDump pathsDump = pathsUpdate.toThrift().getPathsDump();
        Map<Integer, TPathEntry> nodeMap = pathsDump.getNodeMap();
        TPathEntry root = nodeMap.get(pathsDump.getRootId());
        Map<String, Collection<String>> pathImage = new HashMap<>();
        buildPathsImageMap(nodeMap, root, "", pathImage, false);

        assertEquals("/", root.getPathElement());
        assertEquals(4, nodeMap.size());//Tree size
        assertEquals(1, pathImage.size());
        assertEquals(2, pathImage.get("db1.table").size());
        assertEquals(2, localSentryStore.getMPaths().size());

        // Query the persisted path change and ensure it is not saved
        long lastChangeID = localSentryStore.getLastProcessedPathChangeID();
        assertEquals(0, lastChangeID);

        // Delete path 'db1.db/tbl1' from "db1.table1" authzObj.
        UniquePathsUpdate delUpdate = new UniquePathsUpdate("u2", 2, false);
        delUpdate.newPathChange("db1.table").addToDelPaths(Arrays.asList("db1", "tbl1"));
        localSentryStore.deleteAuthzPathsMapping("db1.table", Sets.newHashSet("db1/tbl1"), delUpdate);
        pathsUpdate = localSentryStore.retrieveFullPathsImageUpdate(prefixes);
        pathsDump = pathsUpdate.toThrift().getPathsDump();
        nodeMap = pathsDump.getNodeMap();
        root = nodeMap.get(pathsDump.getRootId());
        pathImage = new HashMap<>();
        buildPathsImageMap(nodeMap, root, "", pathImage, false);

        assertEquals("/", root.getPathElement());
        assertEquals(3, nodeMap.size());//Tree size
        assertEquals(1, pathImage.size());
        assertEquals(1, pathImage.get("db1.table").size());
        assertEquals(1, localSentryStore.getMPaths().size());

        // Query the persisted path change and ensure it is not saved
        lastChangeID = localSentryStore.getLastProcessedPathChangeID();
        assertEquals(0, lastChangeID);

        // Delete "db1.table" authzObj from the authzObj -> [Paths] mapping.
        UniquePathsUpdate delAllupdate = new UniquePathsUpdate("u3", 3, false);
        delAllupdate.newPathChange("db1.table").addToDelPaths(Lists.newArrayList(PathsUpdate.ALL_PATHS));
        localSentryStore.deleteAllAuthzPathsMapping("db1.table", delAllupdate);
        pathsUpdate = localSentryStore.retrieveFullPathsImageUpdate(prefixes);
        pathsDump = pathsUpdate.toThrift().getPathsDump();
        nodeMap = pathsDump.getNodeMap();
        root = nodeMap.get(pathsDump.getRootId());
        pathImage = new HashMap<>();
        buildPathsImageMap(nodeMap, root, "", pathImage, false);

        assertEquals("/", root.getPathElement());
        assertEquals(1, nodeMap.size());//Tree size
        assertEquals(0, pathImage.size());
        assertEquals(0, localSentryStore.getMPaths().size());

        // Query the persisted path change and ensure it is not saved
        lastChangeID = localSentryStore.getLastProcessedPathChangeID();
        assertEquals(0, lastChangeID);

        lastNotificationId = localSentryStore.getLastProcessedNotificationID();
        assertEquals(0, lastNotificationId.longValue());

        // enable HDFS for other tests
        conf.set(ServiceConstants.ServerConfig.PROCESSOR_FACTORIES,
                "org.apache.sentry.hdfs.SentryHDFSServiceProcessorFactory");
        conf.set(ServiceConstants.ServerConfig.SENTRY_POLICY_STORE_PLUGINS, "org.apache.sentry.hdfs.SentryPlugin");
    }

    /**
     * Test retrieveFullPathsImageUpdate() when no image is present.
     * @throws Exception
     */
    @Test
    public void testRetrieveEmptyPathImage() throws Exception {
        String[] prefixes = {};

        PathsUpdate pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        TPathsUpdate tPathsUpdate = pathsUpdate.toThrift();
        TPathsDump pathDump = tPathsUpdate.getPathsDump();
        Map<Integer, TPathEntry> nodeMap = pathDump.getNodeMap();
        assertEquals(1, nodeMap.size());
        System.out.printf(nodeMap.toString());
    }

    /**
     * Test retrieveFullPathsImageUpdate() when a single path is present.
     * @throws Exception
     */
    @Test
    public void testRetrievePathImageWithSingleEntry() throws Exception {
        String prefix = "user/hive/warehouse";
        String[] prefixes = { "/" + prefix };
        Map<String, Collection<String>> authzPaths = new HashMap<>();
        // Makes sure that authorizable object could be associated
        // with different paths and can be properly persisted into database.
        String tablePath = prefix + "/db2.db/table1.1";
        authzPaths.put("db1.table1", Sets.newHashSet(tablePath));
        long notificationID = 1;
        sentryStore.persistFullPathsImage(authzPaths, notificationID);

        PathsUpdate pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        assertEquals(notificationID, pathsUpdate.getImgNum());
        TPathsUpdate tPathsUpdate = pathsUpdate.toThrift();
        TPathsDump pathDump = tPathsUpdate.getPathsDump();
        Map<Integer, TPathEntry> nodeMap = pathDump.getNodeMap();
        System.out.printf(nodeMap.toString());
        assertEquals(6, nodeMap.size());
        int rootId = pathDump.getRootId();
        TPathEntry root = nodeMap.get(rootId);
        assertEquals("/", root.getPathElement());
        List<Integer> children;
        TPathEntry child = root;

        // Walk tree down and verify elements
        for (String path : tablePath.split("/")) {
            children = child.getChildren();
            assertEquals(1, children.size());
            child = nodeMap.get(children.get(0));
            assertEquals(path, child.getPathElement());
        }
    }

    /**
     * Test paths with multiple leading slashes
     * @throws Exception
     */
    @Test
    public void testRetrievePathImageWithMultipleLeadingSlashes() throws Exception {
        //Test with no leading slashes
        String prefix = "user/hive/warehouse";
        String[] prefixes = { "/" + prefix };
        Map<String, Collection<String>> authzPaths = new HashMap<>();
        // Makes sure that authorizable object could be associated
        // with different paths and can be properly persisted into database.
        String tablePath = prefix + "/loc1/db2.db/table1.1";
        authzPaths.put("db1.table1", Sets.newHashSet(tablePath));
        long notificationID = 1;
        sentryStore.persistFullPathsImage(authzPaths, notificationID);
        PathsUpdate pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        assertEquals(notificationID, pathsUpdate.getImgNum());
        TPathsUpdate tPathsUpdate = pathsUpdate.toThrift();
        TPathsDump pathDump = tPathsUpdate.getPathsDump();
        Map<Integer, TPathEntry> nodeMap = pathDump.getNodeMap();
        assertEquals(7, nodeMap.size());

        //Test with single leading slashes
        prefix = "/user/hive/warehouse";
        prefixes = new String[] { prefix };
        authzPaths = new HashMap<>();
        // Makes sure that authorizable object could be associated
        // with different paths and can be properly persisted into database.
        tablePath = prefix + "/loc1/db2.db/table1.1";
        authzPaths.put("db1.table1", Sets.newHashSet(tablePath));
        notificationID = 2;
        sentryStore.persistFullPathsImage(authzPaths, notificationID);
        pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        assertEquals(notificationID, pathsUpdate.getImgNum());
        tPathsUpdate = pathsUpdate.toThrift();
        pathDump = tPathsUpdate.getPathsDump();
        nodeMap = pathDump.getNodeMap();
        assertEquals(7, nodeMap.size());

        //Test with multiple leading slash
        prefix = "///user/hive/warehouse";
        prefixes = new String[] { prefix };
        authzPaths = new HashMap<>();
        // Makes sure that authorizable object could be associated
        // with different paths and can be properly persisted into database.
        tablePath = prefix + "/loc1/db2.db/table1.1";
        authzPaths.put("db1.table1", Sets.newHashSet(tablePath));
        notificationID = 3;
        sentryStore.persistFullPathsImage(authzPaths, notificationID);
        pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        assertEquals(notificationID, pathsUpdate.getImgNum());
        tPathsUpdate = pathsUpdate.toThrift();
        pathDump = tPathsUpdate.getPathsDump();
        nodeMap = pathDump.getNodeMap();
        assertEquals(7, nodeMap.size());
    }

    /**
     * Create a user with the given name and verify that it is created
     *
     * @param userName
     * @throws Exception
     */
    private void createUser(String userName) throws Exception {
        checkUserDoesNotExist(userName);
        sentryStore.createSentryUser(userName);
        checkUserExists(userName);
    }

    /**
     * Fail test if user already exists
     * @param userName User name to checl
     * @throws Exception
     */
    private void checkUserDoesNotExist(String userName) throws Exception {
        try {
            sentryStore.getMSentryUserByName(userName);
            fail("User " + userName + "already exists");
        } catch (SentryNoSuchObjectException e) {
            // Ok
        }
    }

    /**
     * Fail test if user doesn't exist
     * @param userName User name to checl
     * @throws Exception
     */
    private void checkUserExists(String userName) throws Exception {
        assertEquals(userName.toLowerCase(), sentryStore.getMSentryUserByName(userName).getUserName());
    }

    @Test
    public void testGrantRevokePrivilegeForUser() throws Exception {
        String userName = "test-privilege";
        String server = "server1";
        String db = "db1";
        String table = "tbl1";
        createUser(userName);
        TSentryPrivilege privilege = new TSentryPrivilege();
        privilege.setPrivilegeScope("TABLE");
        privilege.setServerName(server);
        privilege.setDbName(db);
        privilege.setTableName(table);
        privilege.setAction(AccessConstants.ALL);
        privilege.setCreateTime(System.currentTimeMillis());
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.USER, userName, Sets.newHashSet(privilege),
                null);
        MSentryUser user = sentryStore.getMSentryUserByName(userName);
        Set<MSentryPrivilege> privileges = user.getPrivileges();
        assertEquals(privileges.toString(), 1, privileges.size());
        privilege.setAction(AccessConstants.SELECT);
        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.USER, userName, Sets.newHashSet(privilege),
                null);
        // after having ALL and revoking SELECT, we should have (INSERT)
        user = sentryStore.getMSentryUserByName(userName);
        privileges = user.getPrivileges();
        assertEquals(privileges.toString(), 1, privileges.size());
        for (MSentryPrivilege mPrivilege : privileges) {
            assertEquals(server, mPrivilege.getServerName());
            assertEquals(db, mPrivilege.getDbName());
            assertEquals(table, mPrivilege.getTableName());
            assertNotSame(AccessConstants.SELECT, mPrivilege.getAction());
            assertFalse(mPrivilege.getGrantOption());
        }
        long numDBPrivs = sentryStore.countMSentryPrivileges();
        assertEquals("Privilege count", numDBPrivs, 1);

        privilege.setAction(AccessConstants.INSERT);

        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.USER, userName, Sets.newHashSet(privilege),
                null);
        user = sentryStore.getMSentryUserByName(userName, false);
        assertNull(user);
    }

    /**
     * Test after granting DB ALL privilege, can still grant table ALL privilege
     * @throws Exception
     */
    @Test
    public void testGrantDuplicatePrivilegeHierchy() throws Exception {
        // grant database all privilege
        String roleName = "test-privilege";
        String server = "server1";
        String db = "db1";
        String table = "tbl1";
        createRole(roleName);
        TSentryPrivilege privilege = new TSentryPrivilege();
        privilege.setPrivilegeScope("DATABASE");
        privilege.setServerName(server);
        privilege.setDbName(db);
        privilege.setAction(AccessConstants.ALL);
        privilege.setCreateTime(System.currentTimeMillis());
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);
        MSentryRole role = sentryStore.getMSentryRoleByName(roleName);
        Set<MSentryPrivilege> privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 1, privileges.size());

        // grant table all privlege
        privilege.setPrivilegeScope("TABLE");
        privilege.setServerName(server.toUpperCase());
        privilege.setDbName(db.toUpperCase());
        privilege.setTableName(table.toUpperCase());
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);

        // check if the table privilege is created
        role = sentryStore.getMSentryRoleByName(roleName);
        privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 2, privileges.size());
    }

    @Test
    public void testListSentryPrivilegesForUsersAndGroups() throws Exception {
        String roleName = "role1";
        String groupName = "list-privs-g1";
        String userName = "u1";
        String grantor = "g1";
        sentryStore.createSentryRole(roleName);

        TSentryPrivilege privilege1 = new TSentryPrivilege();
        privilege1.setPrivilegeScope("TABLE");
        privilege1.setServerName("server1");
        privilege1.setDbName("db1");
        privilege1.setTableName("tbl1");
        privilege1.setAction("SELECT");
        privilege1.setCreateTime(System.currentTimeMillis());
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege1),
                null);

        TSentryPrivilege privilege2 = new TSentryPrivilege();
        privilege2.setPrivilegeScope("SERVER");
        privilege2.setServerName("server1");
        privilege2.setCreateTime(System.currentTimeMillis());
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.USER, userName, Sets.newHashSet(privilege2),
                null);

        Set<TSentryGroup> groups = Sets.newHashSet();
        TSentryGroup group = new TSentryGroup();
        group.setGroupName(groupName);
        groups.add(group);
        sentryStore.alterSentryRoleAddGroups(grantor, roleName, groups);

        // list-privs-g1 has privilege1
        // u1 has privilege2
        Set<TSentryPrivilege> expectedPrivs = new HashSet<>();
        expectedPrivs.add(privilege1);
        expectedPrivs.add(privilege2);

        assertEquals(expectedPrivs, sentryStore.listSentryPrivilegesByUsersAndGroups(Sets.newHashSet(groupName),
                Sets.newHashSet(userName), new TSentryActiveRoleSet(true, new HashSet<>()), null));
    }

    @Test
    public void testListSentryPrivilegesForProviderForUser() throws Exception {
        String userName1 = "list-privs-user1";
        String userName2 = "list-privs-user2";
        sentryStore.createSentryUser(userName1);
        sentryStore.createSentryUser(userName2);

        TSentryPrivilege privilege1 = new TSentryPrivilege();
        privilege1.setPrivilegeScope("TABLE");
        privilege1.setServerName("server1");
        privilege1.setDbName("db1");
        privilege1.setTableName("tbl1");
        privilege1.setAction("SELECT");
        privilege1.setCreateTime(System.currentTimeMillis());
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.USER, userName1, Sets.newHashSet(privilege1),
                null);

        privilege1.setAction("ALL");
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.USER, userName2, Sets.newHashSet(privilege1),
                null);

        assertEquals(Sets.newHashSet("server=server1->db=db1->table=tbl1->action=select"),
                SentryStore.toTrimedLower(sentryStore.listAllSentryPrivilegesForProvider(new HashSet<String>(),
                        Sets.newHashSet(userName1), new TSentryActiveRoleSet(true, new HashSet<String>()))));
    }

    @Test
    public void testListSentryPrivilegesByAuthorizableForUser() throws Exception {
        String userName1 = "list-privs-user1";
        sentryStore.createSentryUser(userName1);

        TSentryPrivilege privilege1 = new TSentryPrivilege();
        privilege1.setPrivilegeScope("TABLE");
        privilege1.setServerName("server1");
        privilege1.setDbName("db1");
        privilege1.setTableName("tbl1");
        privilege1.setAction("SELECT");
        privilege1.setCreateTime(System.currentTimeMillis());
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.USER, userName1, Sets.newHashSet(privilege1),
                null);

        TSentryAuthorizable tSentryAuthorizable = new TSentryAuthorizable();
        tSentryAuthorizable.setServer("server1");
        tSentryAuthorizable.setDb("db1");
        tSentryAuthorizable.setTable("tbl1");

        TSentryPrivilegeMap map = sentryStore.listSentryPrivilegesByAuthorizableForUser(Sets.newHashSet(userName1),
                tSentryAuthorizable, false);
        assertEquals(1, map.getPrivilegeMapSize());
        assertEquals(Sets.newHashSet(userName1), map.getPrivilegeMap().keySet());
    }

    @Test
    public void testGrantRevokePrivilegeMultipleTimesForRole() throws Exception {
        String roleName = "test-privilege";
        String server = "server1";
        String db = "db1";
        String table = "tbl1";
        createRole(roleName);
        TSentryPrivilege privilege = new TSentryPrivilege();
        privilege.setPrivilegeScope("TABLE");
        privilege.setServerName(server);
        privilege.setDbName(db);
        privilege.setTableName(table);
        privilege.setAction(AccessConstants.ALL);
        privilege.setCreateTime(System.currentTimeMillis());
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);

        MSentryRole role = sentryStore.getMSentryRoleByName(roleName);
        Set<MSentryPrivilege> privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 1, privileges.size());

        privilege.setAction(AccessConstants.SELECT);
        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);
        // after having ALL and revoking SELECT, we should have (INSERT)
        role = sentryStore.getMSentryRoleByName(roleName);
        privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 1, privileges.size());

        // second round
        privilege.setAction(AccessConstants.ALL);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);
        List<MSentryPrivilege> totalPrivileges = sentryStore.getAllMSentryPrivileges();
        assertEquals(totalPrivileges.toString(), 1, totalPrivileges.size());

        role = sentryStore.getMSentryRoleByName(roleName);
        privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 1, privileges.size());

        privilege.setAction(AccessConstants.INSERT);
        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.ROLE, roleName, Sets.newHashSet(privilege),
                null);
        // after having ALL and revoking INSERT, we should have (SELECT)
        totalPrivileges = sentryStore.getAllMSentryPrivileges();
        assertEquals(totalPrivileges.toString(), 1, totalPrivileges.size());
        role = sentryStore.getMSentryRoleByName(roleName);
        privileges = role.getPrivileges();
        assertEquals(privileges.toString(), 1, privileges.size());

        sentryStore.dropSentryRole(roleName);
    }

    @Test
    public void testGrantRevokePrivilegeMultipleTimesForUser() throws Exception {
        String userName = "test-privilege";
        String server = "server1";
        String db = "db1";
        String table = "tbl1";
        createUser(userName);
        TSentryPrivilege privilege = new TSentryPrivilege();
        privilege.setPrivilegeScope("TABLE");
        privilege.setServerName(server);
        privilege.setDbName(db);
        privilege.setTableName(table);
        privilege.setAction(AccessConstants.ALL);
        privilege.setCreateTime(System.currentTimeMillis());
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.USER, userName, Sets.newHashSet(privilege),
                null);

        MSentryUser user = sentryStore.getMSentryUserByName(userName);
        Set<MSentryPrivilege> privileges = user.getPrivileges();
        assertEquals(privileges.toString(), 1, privileges.size());

        privilege.setAction(AccessConstants.SELECT);
        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.USER, userName, Sets.newHashSet(privilege),
                null);
        // after having ALL and revoking SELECT, we should have (INSERT)
        user = sentryStore.getMSentryUserByName(userName);
        privileges = user.getPrivileges();
        assertEquals(privileges.toString(), 1, privileges.size());

        // second round
        privilege.setAction(AccessConstants.ALL);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.USER, userName, Sets.newHashSet(privilege),
                null);
        List<MSentryPrivilege> totalPrivileges = sentryStore.getAllMSentryPrivileges();
        assertEquals(totalPrivileges.toString(), 1, totalPrivileges.size());

        user = sentryStore.getMSentryUserByName(userName);
        privileges = user.getPrivileges();
        assertEquals(privileges.toString(), 1, privileges.size());

        privilege.setAction(AccessConstants.INSERT);
        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.USER, userName, Sets.newHashSet(privilege),
                null);
        // after having ALL and revoking INSERT, we should have (SELECT)
        totalPrivileges = sentryStore.getAllMSentryPrivileges();
        assertEquals(totalPrivileges.toString(), 1, totalPrivileges.size());

        user = sentryStore.getMSentryUserByName(userName);
        privileges = user.getPrivileges();
        assertEquals(privileges.toString(), 1, privileges.size());

        privilege.setAction(AccessConstants.SELECT);
        sentryStore.alterSentryRevokePrivileges(SentryPrincipalType.USER, userName, Sets.newHashSet(privilege),
                null);
        // after having ALL and revoking INSERT and SELECT, we should have NO privileges
        // user should be removed automatically
        user = sentryStore.getMSentryUserByName(userName, false);
        assertNull(user);
    }

    @Test
    public void testGetAllRolesPrivileges() throws Exception {
        Map<String, Set<TSentryPrivilege>> allPrivileges;

        // The map must be empty (no null) if no roles exist on the system yet
        allPrivileges = sentryStore.getAllRolesPrivileges();
        assertNotNull(allPrivileges);
        assertTrue(allPrivileges.isEmpty());

        final String ROLE1 = "role1";
        final TSentryPrivilege ROLE1_PRIV1 = toTSentryPrivilege("ALL", "TABLE", "server1", "db1", "table1");
        final TSentryPrivilege ROLE1_PRIV2 = toTSentryPrivilege("SELECT", "TABLE", "server1", "db1", "table2");

        createRole(ROLE1);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, ROLE1, Sets.newHashSet(ROLE1_PRIV1), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, ROLE1, Sets.newHashSet(ROLE1_PRIV2), null);

        final String ROLE2 = "role2";
        final TSentryPrivilege ROLE2_PRIV1 = toTSentryPrivilege("INSERT", "DATABASE", "server1", "db1", "");
        final TSentryPrivilege ROLE2_PRIV2 = toTSentryPrivilege("ALL", "SERVER", "server1", "", "");

        createRole(ROLE2);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, ROLE2, Sets.newHashSet(ROLE2_PRIV1), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, ROLE2, Sets.newHashSet(ROLE2_PRIV2), null);

        final String ROLE3 = "role3";

        createRole(ROLE3);

        allPrivileges = sentryStore.getAllRolesPrivileges();

        // Must return 3 roles, 2 roles has 2 privileges each, 1 role has no privileges
        assertEquals(3, allPrivileges.size());
        assertEquals(2, allPrivileges.get(ROLE1).size());
        assertTrue(allPrivileges.get(ROLE1).contains(ROLE1_PRIV1));
        assertTrue(allPrivileges.get(ROLE1).contains(ROLE1_PRIV2));
        assertEquals(2, allPrivileges.get(ROLE2).size());
        assertTrue(allPrivileges.get(ROLE2).contains(ROLE2_PRIV1));
        assertTrue(allPrivileges.get(ROLE2).contains(ROLE2_PRIV2));
        assertEquals(0, allPrivileges.get(ROLE3).size());
    }

    @Test
    public void testGetAllUsersPrivileges() throws Exception {
        Map<String, Set<TSentryPrivilege>> allPrivileges;

        // The map must be empty (no null) if no roles exist on the system yet
        allPrivileges = sentryStore.getAllUsersPrivileges();
        assertNotNull(allPrivileges);
        assertTrue(allPrivileges.isEmpty());

        final String USER1 = "user1";
        final TSentryPrivilege USER1_PRIV1 = toTSentryPrivilege("ALL", "TABLE", "server1", "db1", "table1");
        final TSentryPrivilege USER1_PRIV2 = toTSentryPrivilege("SELECT", "TABLE", "server1", "db1", "table2");

        createUser(USER1);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.USER, USER1, Sets.newHashSet(USER1_PRIV1), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.USER, USER1, Sets.newHashSet(USER1_PRIV2), null);

        final String USER2 = "user2";
        final TSentryPrivilege USER2_PRIV1 = toTSentryPrivilege("INSERT", "DATABASE", "server1", "db1", "");
        final TSentryPrivilege USER2_PRIV2 = toTSentryPrivilege("ALL", "SERVER", "server1", "", "");

        createUser(USER2);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.USER, USER2, Sets.newHashSet(USER2_PRIV1), null);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.USER, USER2, Sets.newHashSet(USER2_PRIV2), null);

        final String USER3 = "user3";

        createUser(USER3);

        allPrivileges = sentryStore.getAllUsersPrivileges();

        // Must return 3 roles, 2 roles has 2 privileges each, 1 role has no privileges
        assertEquals(3, allPrivileges.size());
        assertEquals(2, allPrivileges.get(USER1).size());
        assertTrue(allPrivileges.get(USER1).contains(USER1_PRIV1));
        assertTrue(allPrivileges.get(USER1).contains(USER1_PRIV2));
        assertEquals(2, allPrivileges.get(USER2).size());
        assertTrue(allPrivileges.get(USER2).contains(USER2_PRIV1));
        assertTrue(allPrivileges.get(USER2).contains(USER2_PRIV2));
        assertEquals(0, allPrivileges.get(USER3).size());
    }

    @Test
    public void testPersistFullPathsImageWithHugeData() throws Exception {
        Map<String, Collection<String>> authzPaths = new HashMap<>();
        String[] prefixes = { "/user/hive/warehouse/" };
        int dbCount = 10, tableCount = 10, partitionCount = 10, prefixSize;
        prefixSize = StringUtils.countMatches(prefixes[0], "/");

        // Makes sure that authorizable object could be associated
        // with different paths and can be properly persisted into database.
        for (int db_index = 1; db_index <= dbCount; db_index++) {
            String db_name = "db" + db_index;
            for (int table_index = 1; table_index <= tableCount; table_index++) {
                Set<String> paths = Sets.newHashSet();
                String table_name = "tb" + table_index;
                String location = prefixes[0] + db_name + "/" + table_name;
                paths.add(location);
                for (int part_index = 1; part_index <= partitionCount; part_index++) {
                    paths.add(location + "/" + part_index);
                }
                authzPaths.put(db_name + table_name, paths);
            }
        }
        long notificationID = 110000;
        LOGGER.debug("Before " + Instant.now());
        sentryStore.persistFullPathsImage(authzPaths, notificationID);
        LOGGER.debug("After " + Instant.now());
        PathsUpdate pathsUpdate = sentryStore.retrieveFullPathsImageUpdate(prefixes);
        assertTrue(pathsUpdate.hasFullImage());
        long savedNotificationID = sentryStore.getLastProcessedNotificationID();
        assertEquals(1, pathsUpdate.getImgNum());
        TPathsDump pathDump = pathsUpdate.toThrift().getPathsDump();
        assertNotNull(pathDump);
        Map<Integer, TPathEntry> nodeMap = pathDump.getNodeMap();
        int nodeCount = prefixSize + dbCount + (dbCount * tableCount) + (dbCount * tableCount * partitionCount);
        assertTrue(nodeMap.size() == nodeCount);
        assertEquals(notificationID, savedNotificationID);
    }

    @Test
    public void testRevokeHiveAllPrivilegeFromImpalaUnset() throws Exception {

        String roleName1 = "impala-r1";
        String serverName = "server1";
        String dbName = "db1";
        String tableName = "tbl1";
        String hiveAll = "*";
        sentryStore.createSentryRole(roleName1);

        TSentryPrivilege hive_privilege_tbl1 = new TSentryPrivilege();
        hive_privilege_tbl1.setPrivilegeScope("TABLE");
        hive_privilege_tbl1.setServerName(serverName);
        hive_privilege_tbl1.setDbName(dbName);
        hive_privilege_tbl1.setTableName(tableName);
        hive_privilege_tbl1.setCreateTime(System.currentTimeMillis());
        hive_privilege_tbl1.setAction(hiveAll);
        hive_privilege_tbl1.setGrantOption(TSentryGrantOption.FALSE);

        TSentryPrivilege impala_privilege_tbl1_unset = new TSentryPrivilege();
        impala_privilege_tbl1_unset.setPrivilegeScope("TABLE");
        impala_privilege_tbl1_unset.setServerName(serverName);
        impala_privilege_tbl1_unset.setDbName(dbName);
        impala_privilege_tbl1_unset.setTableName(tableName);
        impala_privilege_tbl1_unset.setCreateTime(System.currentTimeMillis());
        impala_privilege_tbl1_unset.setAction("ALL");
        impala_privilege_tbl1_unset.setGrantOption(TSentryGrantOption.UNSET);

        TSentryAuthorizable tSentryAuthorizable = new TSentryAuthorizable();
        tSentryAuthorizable.setServer(serverName);
        tSentryAuthorizable.setDb(dbName);
        tSentryAuthorizable.setTable(tableName);

        // grant hive ALL privilege to role roleName1
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(hive_privilege_tbl1), null);

        // revoke impala ALL privilege to role roleName1
        sentryStore.alterSentryRoleRevokePrivileges(roleName1, Sets.newHashSet(impala_privilege_tbl1_unset));
        Map<String, Set<TSentryPrivilege>> rolePrivilegesMap = sentryStore.getRoleNameTPrivilegesMap(dbName,
                tableName);
        assertNotNull(rolePrivilegesMap);
        Set<TSentryPrivilege> rolePrivileges = rolePrivilegesMap.get(roleName1);
        boolean privilegeRevoked = (rolePrivileges == null) || (rolePrivileges.size() == 0);
        assertTrue(privilegeRevoked);
    }

    @Test
    public void testRevokeHiveAllPrivilegeGrantOptionFromImpalaUnset() throws Exception {

        String roleName1 = "impala-r1";
        String serverName = "server1";
        String dbName = "db1";
        String tableName = "tbl1";
        String hiveAll = "*";
        sentryStore.createSentryRole(roleName1);

        TSentryPrivilege hive_privilege_tbl1 = new TSentryPrivilege();
        hive_privilege_tbl1.setPrivilegeScope("TABLE");
        hive_privilege_tbl1.setServerName(serverName);
        hive_privilege_tbl1.setDbName(dbName);
        hive_privilege_tbl1.setTableName(tableName);
        hive_privilege_tbl1.setCreateTime(System.currentTimeMillis());
        hive_privilege_tbl1.setAction(hiveAll);
        hive_privilege_tbl1.setGrantOption(TSentryGrantOption.FALSE);

        TSentryPrivilege impala_privilege_tbl1_unset = new TSentryPrivilege();
        impala_privilege_tbl1_unset.setPrivilegeScope("TABLE");
        impala_privilege_tbl1_unset.setServerName(serverName);
        impala_privilege_tbl1_unset.setDbName(dbName);
        impala_privilege_tbl1_unset.setTableName(tableName);
        impala_privilege_tbl1_unset.setCreateTime(System.currentTimeMillis());
        impala_privilege_tbl1_unset.setAction("ALL");
        impala_privilege_tbl1_unset.setGrantOption(TSentryGrantOption.UNSET);

        TSentryAuthorizable tSentryAuthorizable = new TSentryAuthorizable();
        tSentryAuthorizable.setServer(serverName);
        tSentryAuthorizable.setDb(dbName);
        tSentryAuthorizable.setTable(tableName);

        // grant hive ALL privilege to role roleName1
        hive_privilege_tbl1.setGrantOption(TSentryGrantOption.FALSE);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(hive_privilege_tbl1), null);
        hive_privilege_tbl1.setGrantOption(TSentryGrantOption.TRUE);
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(hive_privilege_tbl1), null);

        // revoke impala ALL privilege to role roleName1
        sentryStore.alterSentryRoleRevokePrivileges(roleName1, Sets.newHashSet(impala_privilege_tbl1_unset));
        Map<String, Set<TSentryPrivilege>> rolePrivilegesMap = sentryStore.getRoleNameTPrivilegesMap(dbName,
                tableName);
        assertNotNull(rolePrivilegesMap);
        Set<TSentryPrivilege> rolePrivileges = rolePrivilegesMap.get(roleName1);
        boolean privilegeRevoked = (rolePrivileges == null) || (rolePrivileges.size() == 0);
        assertTrue(privilegeRevoked);
    }

    @Test
    public void testRevokeHiveAllPrivilegeFromImpala() throws Exception {

        String roleName1 = "impala-r1";
        String serverName = "server1";
        String dbName = "db1";
        String tableName = "tbl1";
        sentryStore.createSentryRole(roleName1);

        TSentryPrivilege hive_privilege_tbl1 = new TSentryPrivilege();
        hive_privilege_tbl1.setPrivilegeScope("TABLE");
        hive_privilege_tbl1.setServerName(serverName);
        hive_privilege_tbl1.setDbName(dbName);
        hive_privilege_tbl1.setTableName(tableName);
        hive_privilege_tbl1.setCreateTime(System.currentTimeMillis());
        hive_privilege_tbl1.setAction("*");
        hive_privilege_tbl1.setGrantOption(TSentryGrantOption.FALSE);

        TSentryPrivilege impala_privilege_tbl1 = new TSentryPrivilege();
        impala_privilege_tbl1.setPrivilegeScope("TABLE");
        impala_privilege_tbl1.setServerName(serverName);
        impala_privilege_tbl1.setDbName(dbName);
        impala_privilege_tbl1.setTableName(tableName);
        impala_privilege_tbl1.setCreateTime(System.currentTimeMillis());
        impala_privilege_tbl1.setAction("ALL");
        impala_privilege_tbl1.setGrantOption(TSentryGrantOption.FALSE);

        TSentryAuthorizable tSentryAuthorizable = new TSentryAuthorizable();
        tSentryAuthorizable.setServer(serverName);
        tSentryAuthorizable.setDb(dbName);
        tSentryAuthorizable.setTable(tableName);

        // grant hive ALL privilege to role roleName1
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(hive_privilege_tbl1), null);

        // revoke impala ALL privilege to role roleName1
        sentryStore.alterSentryRoleRevokePrivileges(roleName1, Sets.newHashSet(impala_privilege_tbl1));
        Map<String, Set<TSentryPrivilege>> rolePrivilegesMap = sentryStore.getRoleNameTPrivilegesMap(dbName,
                tableName);
        assertNotNull(rolePrivilegesMap);
        Set<TSentryPrivilege> rolePrivileges = rolePrivilegesMap.get(roleName1);
        boolean privilegeRevoked = (rolePrivileges == null) || (rolePrivileges.size() == 0);
        assertTrue(privilegeRevoked);
    }

    @Test
    public void testRevokeImpalaAllPrivilegeFromHive() throws Exception {

        String roleName1 = "impala-r1";
        String serverName = "server1";
        String dbName = "db1";
        String tableName = "tbl1";
        sentryStore.createSentryRole(roleName1);

        TSentryPrivilege hive_privilege_tbl1 = new TSentryPrivilege();
        hive_privilege_tbl1.setPrivilegeScope("TABLE");
        hive_privilege_tbl1.setServerName(serverName);
        hive_privilege_tbl1.setDbName(dbName);
        hive_privilege_tbl1.setTableName(tableName);
        hive_privilege_tbl1.setCreateTime(System.currentTimeMillis());
        hive_privilege_tbl1.setAction("*");
        hive_privilege_tbl1.setGrantOption(TSentryGrantOption.FALSE);

        TSentryPrivilege impala_privilege_tbl1 = new TSentryPrivilege();
        impala_privilege_tbl1.setPrivilegeScope("TABLE");
        impala_privilege_tbl1.setServerName(serverName);
        impala_privilege_tbl1.setDbName(dbName);
        impala_privilege_tbl1.setTableName(tableName);
        impala_privilege_tbl1.setCreateTime(System.currentTimeMillis());
        impala_privilege_tbl1.setAction("ALL");
        impala_privilege_tbl1.setGrantOption(TSentryGrantOption.FALSE);

        TSentryAuthorizable tSentryAuthorizable = new TSentryAuthorizable();
        tSentryAuthorizable.setServer(serverName);
        tSentryAuthorizable.setDb(dbName);
        tSentryAuthorizable.setTable(tableName);

        // grant impala ALL privilege to role roleName1
        sentryStore.alterSentryGrantPrivileges(SentryPrincipalType.ROLE, roleName1,
                Sets.newHashSet(impala_privilege_tbl1), null);

        // revoke hive ALL privilege to role roleName1
        sentryStore.alterSentryRoleRevokePrivileges(roleName1, Sets.newHashSet(hive_privilege_tbl1));
        Map<String, Set<TSentryPrivilege>> rolePrivilegesMap = sentryStore.getRoleNameTPrivilegesMap(dbName,
                tableName);
        assertNotNull(rolePrivilegesMap);
        Set<TSentryPrivilege> rolePrivileges = rolePrivilegesMap.get(roleName1);
        boolean privilegeRevoked = (rolePrivileges == null) || (rolePrivileges.size() == 0);
        assertTrue(privilegeRevoked);
    }

    private TSentryPrivilege toTSentryPrivilege(String action, String scope, String server, String dbName,
            String tableName) {
        TSentryPrivilege privilege = new TSentryPrivilege();
        privilege.setPrivilegeScope(scope);
        privilege.setServerName(server);
        privilege.setDbName(dbName);
        privilege.setTableName(tableName);
        privilege.setAction(action);
        privilege.setCreateTime(System.currentTimeMillis());

        return privilege;
    }

    private TSentryPrivilege toTSentryPrivilege(String action, String scope, String server, String dbName,
            String tableName, TSentryGrantOption grantOption) {
        TSentryPrivilege privilege = toTSentryPrivilege(action, scope, server, dbName, tableName);
        privilege.setGrantOption(grantOption);
        return privilege;
    }
}