Repository: phoenix
Updated Branches:
  refs/heads/master 355ee522c -> 88038a2da


http://git-wip-us.apache.org/repos/asf/phoenix/blob/88038a2d/phoenix-core/src/it/java/org/apache/phoenix/end2end/TableDDLPermissionsIT.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/TableDDLPermissionsIT.java
 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/TableDDLPermissionsIT.java
index 971383b..8666bb8 100644
--- 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/TableDDLPermissionsIT.java
+++ 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/TableDDLPermissionsIT.java
@@ -16,144 +16,53 @@
  */
 package org.apache.phoenix.end2end;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import java.io.IOException;
-import java.lang.reflect.UndeclaredThrowableException;
 import java.security.PrivilegedExceptionAction;
 import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
 
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.AuthUtil;
-import org.apache.hadoop.hbase.HBaseTestingUtility;
-import org.apache.hadoop.hbase.HConstants;
 import org.apache.hadoop.hbase.NamespaceDescriptor;
-import org.apache.hadoop.hbase.TableName;
 import org.apache.hadoop.hbase.security.AccessDeniedException;
 import org.apache.hadoop.hbase.security.access.AccessControlClient;
 import org.apache.hadoop.hbase.security.access.Permission.Action;
-import org.apache.hadoop.hbase.util.Bytes;
-import org.apache.hadoop.security.UserGroupInformation;
-import org.apache.phoenix.exception.PhoenixIOException;
-import org.apache.phoenix.query.QueryServices;
-import org.apache.phoenix.util.MetaDataUtil;
 import org.apache.phoenix.util.SchemaUtil;
-import org.junit.After;
-import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import com.google.common.collect.Maps;
 
 /**
  * Test that verifies a user can read Phoenix tables with a minimal set of 
permissions.
  */
 @Category(NeedsOwnMiniClusterTest.class)
-@RunWith(Parameterized.class)
-public class TableDDLPermissionsIT{
-    private static String SUPERUSER;
-
-    private static HBaseTestingUtility testUtil;
-
-    private static final Set<String> PHOENIX_SYSTEM_TABLES = new 
HashSet<>(Arrays.asList(
-            "SYSTEM.CATALOG", "SYSTEM.SEQUENCE", "SYSTEM.STATS", 
"SYSTEM.FUNCTION",
-                "SYSTEM.MUTEX"));
-    // PHOENIX-XXXX SYSTEM.MUTEX isn't being created in the SYSTEM namespace 
as it should be.
-    private static final Set<String> PHOENIX_NAMESPACE_MAPPED_SYSTEM_TABLES = 
new HashSet<>(
-            Arrays.asList("SYSTEM:CATALOG", "SYSTEM:SEQUENCE", "SYSTEM:STATS", 
"SYSTEM:FUNCTION",
-                "SYSTEM.MUTEX"));
-    private static final String GROUP_SYSTEM_ACCESS = "group_system_access";
-    final UserGroupInformation superUser = 
UserGroupInformation.createUserForTesting(SUPERUSER, new String[0]);
-    final UserGroupInformation superUser2 = 
UserGroupInformation.createUserForTesting("superuser", new String[0]);
-    final UserGroupInformation regularUser = 
UserGroupInformation.createUserForTesting("user",  new String[0]);
-    final UserGroupInformation groupUser = 
UserGroupInformation.createUserForTesting("user2", new String[] { 
GROUP_SYSTEM_ACCESS });
-    final UserGroupInformation unprivilegedUser = 
UserGroupInformation.createUserForTesting("unprivilegedUser",
-            new String[0]);
-
+public class TableDDLPermissionsIT extends BasePermissionsIT {
 
-    private static final int NUM_RECORDS = 5;
-
-    private boolean isNamespaceMapped;
-
-    public TableDDLPermissionsIT(final boolean isNamespaceMapped) throws 
Exception {
-        this.isNamespaceMapped = isNamespaceMapped;
-        Map<String, String> clientProps = Maps.newHashMapWithExpectedSize(1);
-        clientProps.put(QueryServices.IS_NAMESPACE_MAPPING_ENABLED, "true");
+    public TableDDLPermissionsIT(boolean isNamespaceMapped) throws Exception {
+        super(isNamespaceMapped);
     }
 
-    private void startNewMiniCluster(Configuration overrideConf) throws 
Exception{
-        if (null != testUtil) {
-            testUtil.shutdownMiniCluster();
-            testUtil = null;
-        }
-        testUtil = new HBaseTestingUtility();
-
-        Configuration config = testUtil.getConfiguration();
-        
-        config.set("hbase.coprocessor.master.classes",
-                "org.apache.hadoop.hbase.security.access.AccessController");
-        config.set("hbase.coprocessor.region.classes",
-                "org.apache.hadoop.hbase.security.access.AccessController");
-        config.set("hbase.coprocessor.regionserver.classes",
-                "org.apache.hadoop.hbase.security.access.AccessController");
-        config.set("hbase.security.exec.permission.checks", "true");
-        config.set("hbase.security.authorization", "true");
-        config.set("hbase.superuser", 
SUPERUSER+","+superUser2.getShortUserName());
-        config.set("hbase.regionserver.wal.codec", 
"org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec");
-        config.set(QueryServices.PHOENIX_ACLS_ENABLED,"true");
-        config.set(QueryServices.IS_NAMESPACE_MAPPING_ENABLED, 
Boolean.toString(isNamespaceMapped));
-        // Avoid multiple clusters trying to bind the master's info port 
(16010)
-        config.setInt(HConstants.MASTER_INFO_PORT, -1);
-        
-        if (overrideConf != null) {
-            config.addResource(overrideConf);
-        }
-        testUtil.startMiniCluster(1);
-    }
-    
-    private void grantSystemTableAccess() throws Exception{
+    private void grantSystemTableAccess() throws Exception {
         try (Connection conn = getConnection()) {
             if (isNamespaceMapped) {
-                grantPermissions(regularUser.getShortUserName(), 
PHOENIX_NAMESPACE_MAPPED_SYSTEM_TABLES, Action.READ,
+                grantPermissions(regularUser1.getShortName(), 
PHOENIX_NAMESPACE_MAPPED_SYSTEM_TABLES, Action.READ,
                         Action.EXEC);
-                grantPermissions(unprivilegedUser.getShortUserName(), 
PHOENIX_NAMESPACE_MAPPED_SYSTEM_TABLES,
+                grantPermissions(unprivilegedUser.getShortName(), 
PHOENIX_NAMESPACE_MAPPED_SYSTEM_TABLES,
                         Action.READ, Action.EXEC);
                 grantPermissions(AuthUtil.toGroupEntry(GROUP_SYSTEM_ACCESS), 
PHOENIX_NAMESPACE_MAPPED_SYSTEM_TABLES,
                         Action.READ, Action.EXEC);
                 // Local Index requires WRITE permission on SYSTEM.SEQUENCE 
TABLE.
-                grantPermissions(regularUser.getShortUserName(), 
Collections.singleton("SYSTEM:SEQUENCE"), Action.WRITE,
+                grantPermissions(regularUser1.getShortName(), 
Collections.singleton("SYSTEM:SEQUENCE"), Action.WRITE,
                         Action.READ, Action.EXEC);
-                grantPermissions(unprivilegedUser.getShortUserName(), 
Collections.singleton("SYSTEM:SEQUENCE"), Action.WRITE,
+                grantPermissions(unprivilegedUser.getShortName(), 
Collections.singleton("SYSTEM:SEQUENCE"), Action.WRITE,
                         Action.READ, Action.EXEC);
                 
             } else {
-                grantPermissions(regularUser.getShortUserName(), 
PHOENIX_SYSTEM_TABLES, Action.READ, Action.EXEC);
-                grantPermissions(unprivilegedUser.getShortUserName(), 
PHOENIX_SYSTEM_TABLES, Action.READ, Action.EXEC);
+                grantPermissions(regularUser1.getShortName(), 
PHOENIX_SYSTEM_TABLES, Action.READ, Action.EXEC);
+                grantPermissions(unprivilegedUser.getShortName(), 
PHOENIX_SYSTEM_TABLES, Action.READ, Action.EXEC);
                 grantPermissions(AuthUtil.toGroupEntry(GROUP_SYSTEM_ACCESS), 
PHOENIX_SYSTEM_TABLES, Action.READ, Action.EXEC);
                 // Local Index requires WRITE permission on SYSTEM.SEQUENCE 
TABLE.
-                grantPermissions(regularUser.getShortUserName(), 
Collections.singleton("SYSTEM.SEQUENCE"), Action.WRITE,
+                grantPermissions(regularUser1.getShortName(), 
Collections.singleton("SYSTEM.SEQUENCE"), Action.WRITE,
                         Action.READ, Action.EXEC);
-                grantPermissions(unprivilegedUser.getShortUserName(), 
Collections.singleton("SYSTEM:SEQUENCE"), Action.WRITE,
+                grantPermissions(unprivilegedUser.getShortName(), 
Collections.singleton("SYSTEM:SEQUENCE"), Action.WRITE,
                         Action.READ, Action.EXEC);
             }
         } catch (Throwable e) {
@@ -165,40 +74,19 @@ public class TableDDLPermissionsIT{
         }
     }
 
-    @Parameters(name = "isNamespaceMapped={0}") // name is used by failsafe as 
file name in reports
-    public static Collection<Boolean> data() {
-        return Arrays.asList(true, false);
-    }
-
-    @BeforeClass
-    public static void doSetup() throws Exception {
-        SUPERUSER = System.getProperty("user.name");
-        //setUpTestDriver(new 
ReadOnlyProps(serverProps.entrySet().iterator()), new 
ReadOnlyProps(clientProps.entrySet().iterator()));
-    }
-
-    protected static String getUrl() {
-        return "jdbc:phoenix:localhost:" + 
testUtil.getZkCluster().getClientPort() + ":/hbase";
-    }
-
-    public Connection getConnection() throws SQLException{
-        Properties props = new Properties();
-        props.setProperty(QueryServices.IS_NAMESPACE_MAPPING_ENABLED, 
Boolean.toString(isNamespaceMapped));
-        return DriverManager.getConnection(getUrl(),props);
-    }
-
     @Test
     public void testSchemaPermissions() throws Throwable{
 
         if (!isNamespaceMapped) { return; }
         try {
-            startNewMiniCluster(null);
+            startNewMiniCluster();
             grantSystemTableAccess();
             final String schemaName = "TEST_SCHEMA_PERMISSION";
-            superUser.doAs(new PrivilegedExceptionAction<Void>() {
+            superUser1.runAs(new PrivilegedExceptionAction<Void>() {
                 @Override
                 public Void run() throws Exception {
                     try {
-                        
AccessControlClient.grant(getUtility().getConnection(), 
regularUser.getShortUserName(),
+                        
AccessControlClient.grant(getUtility().getConnection(), 
regularUser1.getShortName(),
                                 Action.ADMIN);
                     } catch (Throwable e) {
                         if (e instanceof Exception) {
@@ -210,26 +98,20 @@ public class TableDDLPermissionsIT{
                     return null;
                 }
             });
-            verifyAllowed(createSchema(schemaName), regularUser);
+            verifyAllowed(createSchema(schemaName), regularUser1);
             // Unprivileged user cannot drop a schema
-            verifyDenied(dropSchema(schemaName), unprivilegedUser);
-            verifyDenied(createSchema(schemaName), unprivilegedUser);
+            verifyDenied(dropSchema(schemaName), AccessDeniedException.class, 
unprivilegedUser);
+            verifyDenied(createSchema(schemaName), 
AccessDeniedException.class, unprivilegedUser);
 
-            verifyAllowed(dropSchema(schemaName), regularUser);
+            verifyAllowed(dropSchema(schemaName), regularUser1);
         } finally {
             revokeAll();
         }
     }
 
     @Test
-    public void testAutomaticGrantDisabled() throws Throwable{
-        testIndexAndView(false);
-    }
-    
-    public void testIndexAndView(boolean isAutomaticGrant) throws Throwable {
-        Configuration conf = new Configuration();
-        conf.set(QueryServices.PHOENIX_AUTOMATIC_GRANT_ENABLED, 
Boolean.toString(isAutomaticGrant));
-        startNewMiniCluster(conf);
+    public void testAutomaticGrantWithIndexAndView() throws Throwable {
+        startNewMiniCluster();
         final String schema = "TEST_INDEX_VIEW";
         final String tableName = "TABLE_DDL_PERMISSION_IT";
         final String phoenixTableName = schema + "." + tableName;
@@ -244,17 +126,17 @@ public class TableDDLPermissionsIT{
         final String viewIndexName2 = tableName + "_VIDX2";
         grantSystemTableAccess();
         try {
-            superUser.doAs(new PrivilegedExceptionAction<Void>() {
+            superUser1.runAs(new PrivilegedExceptionAction<Void>() {
                 @Override
                 public Void run() throws Exception {
                     try {
-                        verifyAllowed(createSchema(schema), superUser);
+                        verifyAllowed(createSchema(schema), superUser1);
                         if (isNamespaceMapped) {
-                            grantPermissions(regularUser.getShortUserName(), 
schema, Action.CREATE);
+                            grantPermissions(regularUser1.getShortName(), 
schema, Action.CREATE);
                             
grantPermissions(AuthUtil.toGroupEntry(GROUP_SYSTEM_ACCESS), schema, 
Action.CREATE);
 
                         } else {
-                            grantPermissions(regularUser.getShortUserName(),
+                            grantPermissions(regularUser1.getShortName(),
                                     
NamespaceDescriptor.DEFAULT_NAMESPACE.getName(), Action.CREATE);
                             
grantPermissions(AuthUtil.toGroupEntry(GROUP_SYSTEM_ACCESS),
                                     
NamespaceDescriptor.DEFAULT_NAMESPACE.getName(), Action.CREATE);
@@ -271,29 +153,29 @@ public class TableDDLPermissionsIT{
                 }
             });
 
-            verifyAllowed(createTable(phoenixTableName), regularUser);
-            verifyAllowed(createIndex(indexName1, phoenixTableName), 
regularUser);
-            verifyAllowed(createView(viewName1, phoenixTableName), 
regularUser);
-            verifyAllowed(createLocalIndex(lIndexName1, phoenixTableName), 
regularUser);
-            verifyAllowed(createIndex(viewIndexName1, viewName1), regularUser);
-            verifyAllowed(createIndex(viewIndexName2, viewName1), regularUser);
-            verifyAllowed(createView(viewName4, viewName1), regularUser);
-            verifyAllowed(readTable(phoenixTableName), regularUser);
-
-            verifyDenied(createIndex(indexName2, phoenixTableName), 
unprivilegedUser);
-            verifyDenied(createView(viewName2, phoenixTableName), 
unprivilegedUser);
-            verifyDenied(createView(viewName3, viewName1), unprivilegedUser);
-            verifyDenied(dropView(viewName1), unprivilegedUser);
+            verifyAllowed(createTable(phoenixTableName), regularUser1);
+            verifyAllowed(createIndex(indexName1, phoenixTableName), 
regularUser1);
+            verifyAllowed(createView(viewName1, phoenixTableName), 
regularUser1);
+            verifyAllowed(createLocalIndex(lIndexName1, phoenixTableName), 
regularUser1);
+            verifyAllowed(createIndex(viewIndexName1, viewName1), 
regularUser1);
+            verifyAllowed(createIndex(viewIndexName2, viewName1), 
regularUser1);
+            verifyAllowed(createView(viewName4, viewName1), regularUser1);
+            verifyAllowed(readTable(phoenixTableName), regularUser1);
+
+            verifyDenied(createIndex(indexName2, phoenixTableName), 
AccessDeniedException.class, unprivilegedUser);
+            verifyDenied(createView(viewName2, 
phoenixTableName),AccessDeniedException.class,  unprivilegedUser);
+            verifyDenied(createView(viewName3, viewName1), 
AccessDeniedException.class, unprivilegedUser);
+            verifyDenied(dropView(viewName1), AccessDeniedException.class, 
unprivilegedUser);
             
-            verifyDenied(dropIndex(indexName1, phoenixTableName), 
unprivilegedUser);
-            verifyDenied(dropTable(phoenixTableName), unprivilegedUser);
-            verifyDenied(rebuildIndex(indexName1, phoenixTableName), 
unprivilegedUser);
-            verifyDenied(addColumn(phoenixTableName, "val1"), 
unprivilegedUser);
-            verifyDenied(dropColumn(phoenixTableName, "val"), 
unprivilegedUser);
-            verifyDenied(addProperties(phoenixTableName, "GUIDE_POSTS_WIDTH", 
"100"), unprivilegedUser);
+            verifyDenied(dropIndex(indexName1, phoenixTableName), 
AccessDeniedException.class, unprivilegedUser);
+            verifyDenied(dropTable(phoenixTableName), 
AccessDeniedException.class, unprivilegedUser);
+            verifyDenied(rebuildIndex(indexName1, phoenixTableName), 
AccessDeniedException.class, unprivilegedUser);
+            verifyDenied(addColumn(phoenixTableName, "val1"), 
AccessDeniedException.class, unprivilegedUser);
+            verifyDenied(dropColumn(phoenixTableName, "val"), 
AccessDeniedException.class, unprivilegedUser);
+            verifyDenied(addProperties(phoenixTableName, "GUIDE_POSTS_WIDTH", 
"100"), AccessDeniedException.class, unprivilegedUser);
 
             // Granting read permission to unprivileged user, now he should be 
able to create view but not index
-            grantPermissions(unprivilegedUser.getShortUserName(),
+            grantPermissions(unprivilegedUser.getShortName(),
                     Collections.singleton(
                             SchemaUtil.getPhysicalHBaseTableName(schema, 
tableName, isNamespaceMapped).getString()),
                     Action.READ, Action.EXEC);
@@ -301,52 +183,18 @@ public class TableDDLPermissionsIT{
                     Collections.singleton(
                             SchemaUtil.getPhysicalHBaseTableName(schema, 
tableName, isNamespaceMapped).getString()),
                     Action.READ, Action.EXEC);
-            verifyDenied(createIndex(indexName2, phoenixTableName), 
unprivilegedUser);
-            if (!isAutomaticGrant) {
-                // Automatic grant will read access for all indexes
-                verifyDenied(createView(viewName2, phoenixTableName), 
unprivilegedUser);
-
-                // Granting read permission to unprivileged user on index so 
that a new view can read a index as well,
-                // now
-                // he should be able to create view but not index
-                grantPermissions(unprivilegedUser.getShortUserName(),
-                        Collections.singleton(SchemaUtil
-                                .getPhysicalHBaseTableName(schema, indexName1, 
isNamespaceMapped).getString()),
-                        Action.READ, Action.EXEC);
-                verifyDenied(createView(viewName3, viewName1), 
unprivilegedUser);
-            }
-            
+            verifyDenied(createIndex(indexName2, phoenixTableName), 
AccessDeniedException.class, unprivilegedUser);
             verifyAllowed(createView(viewName2, phoenixTableName), 
unprivilegedUser);
-            
-            if (!isAutomaticGrant) {
-                // Grant access to view index for parent view
-                grantPermissions(unprivilegedUser.getShortUserName(),
-                        
Collections.singleton(Bytes.toString(MetaDataUtil.getViewIndexPhysicalName(SchemaUtil
-                                .getPhysicalHBaseTableName(schema, tableName, 
isNamespaceMapped).getBytes()))),
-                        Action.READ, Action.EXEC);
-            }
             verifyAllowed(createView(viewName3, viewName1), unprivilegedUser);
             
             // Grant create permission in namespace
             if (isNamespaceMapped) {
-                grantPermissions(unprivilegedUser.getShortUserName(), schema, 
Action.CREATE);
+                grantPermissions(unprivilegedUser.getShortName(), schema, 
Action.CREATE);
             } else {
-                grantPermissions(unprivilegedUser.getShortUserName(), 
NamespaceDescriptor.DEFAULT_NAMESPACE.getName(),
+                grantPermissions(unprivilegedUser.getShortName(), 
NamespaceDescriptor.DEFAULT_NAMESPACE.getName(),
                         Action.CREATE);
             }
-            if (!isAutomaticGrant) {
-                verifyDenied(createIndex(indexName2, phoenixTableName), 
unprivilegedUser);
-                // Give user of data table access to index table which will be 
created by unprivilegedUser
-                grantPermissions(regularUser.getShortUserName(),
-                        Collections.singleton(SchemaUtil
-                                .getPhysicalHBaseTableName(schema, indexName2, 
isNamespaceMapped).getString()),
-                        Action.WRITE);
-                verifyDenied(createIndex(indexName2, phoenixTableName), 
unprivilegedUser);
-                grantPermissions(regularUser.getShortUserName(),
-                        Collections.singleton(SchemaUtil
-                                .getPhysicalHBaseTableName(schema, indexName2, 
isNamespaceMapped).getString()),
-                        Action.WRITE, Action.READ, Action.CREATE, Action.EXEC, 
Action.ADMIN);
-            }
+
             // we should be able to read the data from another index as well 
to which we have not given any access to
             // this user
             verifyAllowed(createIndex(indexName2, phoenixTableName), 
unprivilegedUser);
@@ -355,19 +203,19 @@ public class TableDDLPermissionsIT{
             verifyAllowed(rebuildIndex(indexName2, phoenixTableName), 
unprivilegedUser);
 
             // data table user should be able to read new index
-            verifyAllowed(rebuildIndex(indexName2, phoenixTableName), 
regularUser);
-            verifyAllowed(readTable(phoenixTableName, indexName2), 
regularUser);
-
-            verifyAllowed(readTable(phoenixTableName), regularUser);
-            verifyAllowed(rebuildIndex(indexName1, phoenixTableName), 
regularUser);
-            verifyAllowed(addColumn(phoenixTableName, "val1"), regularUser);
-            verifyAllowed(addProperties(phoenixTableName, "GUIDE_POSTS_WIDTH", 
"100"), regularUser);
-            verifyAllowed(dropView(viewName1), regularUser);
-            verifyAllowed(dropView(viewName2), regularUser);
-            verifyAllowed(dropColumn(phoenixTableName, "val1"), regularUser);
-            verifyAllowed(dropIndex(indexName2, phoenixTableName), 
regularUser);
-            verifyAllowed(dropIndex(indexName1, phoenixTableName), 
regularUser);
-            verifyAllowed(dropTable(phoenixTableName), regularUser);
+            verifyAllowed(rebuildIndex(indexName2, phoenixTableName), 
regularUser1);
+            verifyAllowed(readTable(phoenixTableName, indexName2), 
regularUser1);
+
+            verifyAllowed(readTable(phoenixTableName), regularUser1);
+            verifyAllowed(rebuildIndex(indexName1, phoenixTableName), 
regularUser1);
+            verifyAllowed(addColumn(phoenixTableName, "val1"), regularUser1);
+            verifyAllowed(addProperties(phoenixTableName, "GUIDE_POSTS_WIDTH", 
"100"), regularUser1);
+            verifyAllowed(dropView(viewName1), regularUser1);
+            verifyAllowed(dropView(viewName2), regularUser1);
+            verifyAllowed(dropColumn(phoenixTableName, "val1"), regularUser1);
+            verifyAllowed(dropIndex(indexName2, phoenixTableName), 
regularUser1);
+            verifyAllowed(dropIndex(indexName1, phoenixTableName), 
regularUser1);
+            verifyAllowed(dropTable(phoenixTableName), regularUser1);
 
             // check again with super users
             verifyAllowed(createTable(phoenixTableName), superUser2);
@@ -381,312 +229,5 @@ public class TableDDLPermissionsIT{
             revokeAll();
         }
     }
-    
-    
-    @Test
-    public void testAutomaticGrantEnabled() throws Throwable{
-        testIndexAndView(true);
-    }
-
-    private void revokeAll() throws IOException, Throwable {
-        AccessControlClient.revoke(getUtility().getConnection(), 
AuthUtil.toGroupEntry(GROUP_SYSTEM_ACCESS),Action.values() );
-        AccessControlClient.revoke(getUtility().getConnection(), 
regularUser.getShortUserName(),Action.values() );
-        AccessControlClient.revoke(getUtility().getConnection(), 
unprivilegedUser.getShortUserName(),Action.values() );
-        
-    }
-
-    protected void grantPermissions(String groupEntry, Action... actions) 
throws IOException, Throwable {
-        AccessControlClient.grant(getUtility().getConnection(), groupEntry, 
actions);
-    }
-
-    private AccessTestAction dropTable(final String tableName) throws 
SQLException {
-        return new AccessTestAction() {
-            @Override
-            public Object run() throws Exception {
-                try (Connection conn = getConnection(); Statement stmt = 
conn.createStatement();) {
-                    assertFalse(stmt.execute("DROP TABLE IF EXISTS " + 
tableName));
-                }
-                return null;
-            }
-        };
-
-    }
-
-    private AccessTestAction createTable(final String tableName) throws 
SQLException {
-        return new AccessTestAction() {
-            @Override
-            public Object run() throws Exception {
-        try (Connection conn = getConnection(); Statement stmt = 
conn.createStatement();) {
-            assertFalse(stmt.execute("CREATE TABLE " + tableName + "(pk 
INTEGER not null primary key, data VARCHAR,val integer)"));
-            try (PreparedStatement pstmt = conn.prepareStatement("UPSERT INTO 
" + tableName + " values(?, ?, ?)")) {
-                for (int i = 0; i < NUM_RECORDS; i++) {
-                    pstmt.setInt(1, i);
-                    pstmt.setString(2, Integer.toString(i));
-                    pstmt.setInt(3, i);
-                    assertEquals(1, pstmt.executeUpdate());
-                }
-            }
-            conn.commit();
-        }
-        return null;
-            }
-        };
-    }
-
-    private AccessTestAction readTable(final String tableName) throws 
SQLException {
-        return readTable(tableName,null);
-    }
-    private AccessTestAction readTable(final String tableName, final String 
indexName) throws SQLException {
-        return new AccessTestAction() {
-            @Override
-            public Object run() throws Exception {
-                try (Connection conn = getConnection(); Statement stmt = 
conn.createStatement()) {
-                    ResultSet rs = stmt.executeQuery("SELECT 
"+(indexName!=null?"/*+ INDEX("+tableName+" "+indexName+")*/":"")+" pk, 
data,val FROM " + tableName +" where data>='0'");
-                    assertNotNull(rs);
-                    int i = 0;
-                    while (rs.next()) {
-                        assertEquals(i, rs.getInt(1));
-                        assertEquals(Integer.toString(i), rs.getString(2));
-                        assertEquals(i, rs.getInt(3));
-                        i++;
-                    }
-                    assertEquals(NUM_RECORDS, i);
-                }
-                return null;
-                }
-            };
-    }
-
-    public static HBaseTestingUtility getUtility(){
-        return testUtil;
-    }
-
-    private void grantPermissions(String toUser, Set<String> tablesToGrant, 
Action... actions) throws Throwable {
-        for (String table : tablesToGrant) {
-            AccessControlClient.grant(getUtility().getConnection(), 
TableName.valueOf(table), toUser, null, null,
-                    actions);
-        }
-    }
-
-    private void grantPermissions(String toUser, String namespace, Action... 
actions) throws Throwable {
-        AccessControlClient.grant(getUtility().getConnection(), namespace, 
toUser, actions);
-    }
-    
-
-    private AccessTestAction dropColumn(final String tableName, final String 
columnName) throws SQLException {
-        return new AccessTestAction() {
-            @Override
-            public Object run() throws Exception {
-                try (Connection conn = getConnection(); Statement stmt = 
conn.createStatement();) {
-                    assertFalse(stmt.execute("ALTER TABLE " + tableName + " 
DROP COLUMN "+columnName));
-                }
-                return null;
-            }
-        };
-    }
-
-    private AccessTestAction addColumn(final String tableName, final String 
columnName) throws SQLException {
-        return new AccessTestAction() {
-            @Override
-            public Object run() throws Exception {
-                try (Connection conn = getConnection(); Statement stmt = 
conn.createStatement();) {
-                    assertFalse(stmt.execute("ALTER TABLE " + tableName + " 
ADD "+columnName+" varchar"));
-                }
-                return null;
-            }
-        };
-    }
-
-    private AccessTestAction addProperties(final String tableName, final 
String property, final String value)
-            throws SQLException {
-        return new AccessTestAction() {
-            @Override
-            public Object run() throws Exception {
-                try (Connection conn = getConnection(); Statement stmt = 
conn.createStatement();) {
-                    assertFalse(stmt.execute("ALTER TABLE " + tableName + " 
SET " + property + "=" + value));
-                }
-                return null;
-            }
-        };
-    }
-
-    private AccessTestAction dropView(final String viewName) throws 
SQLException {
-        return new AccessTestAction() {
-            @Override
-            public Object run() throws Exception {
-                try (Connection conn = getConnection(); Statement stmt = 
conn.createStatement();) {
-                    assertFalse(stmt.execute("DROP VIEW " + viewName));
-                }
-                return null;
-            }
-        };
-    }
-
-    private AccessTestAction createView(final String viewName, final String 
dataTable) throws SQLException {
-        return new AccessTestAction() {
-            @Override
-            public Object run() throws Exception {
-                try (Connection conn = getConnection(); Statement stmt = 
conn.createStatement();) {
-                    assertFalse(stmt.execute("CREATE VIEW " + viewName + " AS 
SELECT * FROM " + dataTable));
-                }
-                return null;
-            }
-        };
-    }
-
-    private AccessTestAction createIndex(final String indexName, final String 
dataTable) throws SQLException {
-        return new AccessTestAction() {
-            @Override
-            public Object run() throws Exception {
-
-                try (Connection conn = getConnection(); Statement stmt = 
conn.createStatement();) {
-                    assertFalse(stmt.execute("CREATE INDEX " + indexName + " 
on " + dataTable + "(data)"));
-                }
-                return null;
-            }
-        };
-    }
-    
-    private AccessTestAction createLocalIndex(final String indexName, final 
String dataTable) throws SQLException {
-        return new AccessTestAction() {
-            @Override
-            public Object run() throws Exception {
-
-                try (Connection conn = getConnection(); Statement stmt = 
conn.createStatement();) {
-                    assertFalse(stmt.execute("CREATE LOCAL INDEX " + indexName 
+ " on " + dataTable + "(data)"));
-                }
-                return null;
-            }
-        };
-    }
-
-    private AccessTestAction dropIndex(final String indexName, final String 
dataTable) throws SQLException {
-        return new AccessTestAction() {
-            @Override
-            public Object run() throws Exception {
-                try (Connection conn = getConnection(); Statement stmt = 
conn.createStatement();) {
-                    assertFalse(stmt.execute("DROP INDEX " + indexName + " on 
" + dataTable));
-                }
-                return null;
-            }
-        };
-    }
-
-    private AccessTestAction createSchema(final String schemaName) throws 
SQLException {
-        return new AccessTestAction() {
-            @Override
-            public Object run() throws Exception {
-                if (isNamespaceMapped) {
-                    try (Connection conn = getConnection(); Statement stmt = 
conn.createStatement();) {
-                        assertFalse(stmt.execute("CREATE SCHEMA " + 
schemaName));
-                    }
-                }
-                return null;
-            }
-        };
-    }
-
-    private AccessTestAction dropSchema(final String schemaName) throws 
SQLException {
-        return new AccessTestAction() {
-            @Override
-            public Object run() throws Exception {
-                if (isNamespaceMapped) {
-                    try (Connection conn = getConnection(); Statement stmt = 
conn.createStatement();) {
-                        assertFalse(stmt.execute("DROP SCHEMA " + schemaName));
-                    }
-                }
-                return null;
-            }
-        };
-    }
-
-    private AccessTestAction rebuildIndex(final String indexName, final String 
dataTable) throws SQLException {
-        return new AccessTestAction() {
-            @Override
-            public Object run() throws Exception {
-                try (Connection conn = getConnection(); Statement stmt = 
conn.createStatement();) {
-                    assertFalse(stmt.execute("ALTER INDEX " + indexName + " on 
" + dataTable + " DISABLE"));
-                    assertFalse(stmt.execute("ALTER INDEX " + indexName + " on 
" + dataTable + " REBUILD"));
-                }
-                return null;
-            }
-        };
-    }
-
-    static interface AccessTestAction extends 
PrivilegedExceptionAction<Object> { }
-
-    @After
-    public void cleanup() throws Exception {
-        if (null != testUtil) {
-          testUtil.shutdownMiniCluster();
-          testUtil = null;
-        }
-    }
-
-    /** This fails only in case of ADE or empty list for any of the users. */
-    private void verifyAllowed(AccessTestAction action, 
UserGroupInformation... users) throws Exception {
-      for (UserGroupInformation user : users) {
-        verifyAllowed(user, action);
-      }
-    }
-
-    /** This passes only in case of ADE for all users. */
-    private void verifyDenied(AccessTestAction action, UserGroupInformation... 
users) throws Exception {
-      for (UserGroupInformation user : users) {
-        verifyDenied(user, action);
-      }
-    }
-
-    /** This fails only in case of ADE or empty list for any of the actions. */
-    private void verifyAllowed(UserGroupInformation user, AccessTestAction... 
actions) throws Exception {
-      for (AccessTestAction action : actions) {
-        try {
-          Object obj = user.doAs(action);
-          if (obj != null && obj instanceof List<?>) {
-            List<?> results = (List<?>) obj;
-            if (results != null && results.isEmpty()) {
-              fail("Empty non null results from action for user '" + 
user.getShortUserName() + "'");
-            }
-          }
-        } catch (AccessDeniedException ade) {
-          fail("Expected action to pass for user '" + user.getShortUserName() 
+ "' but was denied");
-        }
-      }
-    }
-
-    /** This passes only in case of ADE for all actions. */
-    private void verifyDenied(UserGroupInformation user, AccessTestAction... 
actions) throws Exception {
-        for (AccessTestAction action : actions) {
-            try {
-                user.doAs(action);
-                fail("Expected exception was not thrown for user '" + 
user.getShortUserName() + "'");
-            } catch (IOException e) {
-                fail("Expected exception was not thrown for user '" + 
user.getShortUserName() + "'");
-            } catch (UndeclaredThrowableException ute) {
-                Throwable ex = ute.getUndeclaredThrowable();
-
-                if (ex instanceof PhoenixIOException) {
-                    if (ex.getCause() instanceof AccessDeniedException) {
-                        // expected result
-                        validateAccessDeniedException((AccessDeniedException) 
ex.getCause());
-                        return;
-                    }
-                }
-            }catch(RuntimeException ex){
-                // This can occur while accessing tabledescriptors from client 
by the unprivileged user
-                if (ex.getCause() instanceof AccessDeniedException) {
-                    // expected result
-                    validateAccessDeniedException((AccessDeniedException) 
ex.getCause());
-                    return;
-                }
-            }
-            fail("Expected exception was not thrown for user '" + 
user.getShortUserName() + "'");
-        }
-    }
 
-    private void validateAccessDeniedException(AccessDeniedException ade) {
-        String msg = ade.getMessage();
-        assertTrue("Exception contained unexpected message: '" + msg + "'",
-            !msg.contains("is not the scanner owner"));
-    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/88038a2d/phoenix-core/src/main/antlr3/PhoenixSQL.g
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/antlr3/PhoenixSQL.g 
b/phoenix-core/src/main/antlr3/PhoenixSQL.g
index 93e0ede..8c9c135 100644
--- a/phoenix-core/src/main/antlr3/PhoenixSQL.g
+++ b/phoenix-core/src/main/antlr3/PhoenixSQL.g
@@ -118,6 +118,7 @@ tokens
     UNION='union';
     FUNCTION='function';
     AS='as';
+    TO='to';
     TEMPORARY='temporary';
     RETURNS='returns';
     USING='using';
@@ -144,6 +145,8 @@ tokens
     DUPLICATE = 'duplicate';
     IGNORE = 'ignore';
     IMMUTABLE = 'immutable';
+    GRANT = 'grant';
+    REVOKE = 'revoke';
 }
 
 
@@ -430,6 +433,8 @@ oneStatement returns [BindableStatement ret]
     |   s=delete_jar_node
     |   s=alter_session_node
     |  s=create_sequence_node
+    |   s=grant_permission_node
+    |   s=revoke_permission_node
     |  s=drop_sequence_node
     |  s=drop_schema_node
     |  s=use_schema_node
@@ -458,6 +463,30 @@ create_schema_node returns [CreateSchemaStatement ret]
         {ret = factory.createSchema(s, ex!=null); }
     ;
 
+// Parse a grant permission statement
+grant_permission_node returns [ChangePermsStatement ret]
+    :   GRANT p=literal (ON ((TABLE)? table=table_name | s=SCHEMA 
schema=identifier))? TO (g=GROUP)? ug=literal
+        {
+            String permsString = SchemaUtil.normalizeLiteral(p);
+            if (permsString != null && permsString.length() > 5) {
+                throw new RuntimeException("Permissions String length should 
be less than 5 characters");
+            }
+            $ret = factory.changePermsStatement(permsString, s!=null, table, 
schema, g!=null, ug, Boolean.TRUE);
+        }
+    ;
+
+// Parse a revoke permission statement
+revoke_permission_node returns [ChangePermsStatement ret]
+    :   REVOKE (p=literal)? (ON ((TABLE)? table=table_name | s=SCHEMA 
schema=identifier))? FROM (g=GROUP)? ug=literal
+        {
+            String permsString = SchemaUtil.normalizeLiteral(p);
+            if (permsString != null && permsString.length() > 5) {
+                throw new RuntimeException("Permissions String length should 
be less than 5 characters");
+            }
+            $ret = factory.changePermsStatement(permsString, s!=null, table, 
schema, g!=null, ug, Boolean.FALSE);
+        }
+    ;
+
 // Parse a create view statement.
 create_view_node returns [CreateTableStatement ret]
     :   CREATE VIEW (IF NOT ex=EXISTS)? t=from_table_name 
@@ -1161,7 +1190,6 @@ BIND_NAME
     : COLON (DIGIT)+
     ;
 
-
 NAME
     :    LETTER (FIELDCHAR)*
     |    '\"' (DBL_QUOTE_CHAR)* '\"'

http://git-wip-us.apache.org/repos/asf/phoenix/blob/88038a2d/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/PhoenixAccessController.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/PhoenixAccessController.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/PhoenixAccessController.java
index 8437b37..a4bc857 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/PhoenixAccessController.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/PhoenixAccessController.java
@@ -75,8 +75,6 @@ public class PhoenixAccessController extends 
BaseMetaDataEndpointObserver {
     private ArrayList<BaseMasterAndRegionObserver> accessControllers;
     private boolean accessCheckEnabled;
     private UserProvider userProvider;
-    private boolean isAutomaticGrantEnabled;
-    private boolean isStrictMode;
     public static final Log LOG = 
LogFactory.getLog(PhoenixAccessController.class);
     private static final Log AUDITLOG =
             
LogFactory.getLog("SecurityLogger."+PhoenixAccessController.class.getName());
@@ -114,8 +112,6 @@ public class PhoenixAccessController extends 
BaseMetaDataEndpointObserver {
         Configuration conf = env.getConfiguration();
         this.accessCheckEnabled = 
conf.getBoolean(QueryServices.PHOENIX_ACLS_ENABLED,
                 QueryServicesOptions.DEFAULT_PHOENIX_ACLS_ENABLED);
-        
this.isAutomaticGrantEnabled=conf.getBoolean(QueryServices.PHOENIX_AUTOMATIC_GRANT_ENABLED,
-                QueryServicesOptions.DEFAULT_PHOENIX_AUTOMATIC_GRANT_ENABLED);
         if (!this.accessCheckEnabled) {
             LOG.warn("PhoenixAccessController has been loaded with 
authorization checks disabled.");
         }
@@ -127,8 +123,6 @@ public class PhoenixAccessController extends 
BaseMetaDataEndpointObserver {
         }
         // set the user-provider.
         this.userProvider = UserProvider.instantiate(env.getConfiguration());
-        this.isStrictMode = 
conf.getBoolean(QueryServices.PHOENIX_SECURITY_PERMISSION_STRICT_MODE_ENABLED,
-                
QueryServicesOptions.DEFAULT_PHOENIX_SECURITY_PERMISSION_STRICT_MODE_ENABLED);
         // init superusers and add the server principal (if using security)
         // or process owner as default super user.
         Superusers.initialize(env.getConfiguration());
@@ -223,23 +217,12 @@ public class PhoenixAccessController extends 
BaseMetaDataEndpointObserver {
     public void handleRequireAccessOnDependentTable(String request, String 
userName, TableName dependentTable,
             String requestTable, Set<Action> requireAccess, Set<Action> 
accessExists) throws IOException {
 
-        if (!isStrictMode) {
-            AUDITLOG.warn("Strict mode is not enabled, so " + request + " is 
allowed but User:" + userName
-                    + " will not have following access " + requireAccess + " 
to the existing dependent physical table "
-                    + dependentTable);
-            return;
-        }
-        if (isAutomaticGrantEnabled) {
-            Set<Action> unionSet = new HashSet<Action>();
-            unionSet.addAll(requireAccess);
-            unionSet.addAll(accessExists);
-            AUDITLOG.info(request + ": Automatically granting access to index 
table during creation of view:"
-                    + requestTable + authString(userName, dependentTable, 
requireAccess));
-            grantPermissions(userName, dependentTable.getName(), 
unionSet.toArray(new Action[0]));
-        } else {
-            throw new AccessDeniedException(
-                    "Insufficient permissions for users of dependent table" + 
authString(userName, dependentTable, requireAccess));
-        }
+        Set<Action> unionSet = new HashSet<Action>();
+        unionSet.addAll(requireAccess);
+        unionSet.addAll(accessExists);
+        AUDITLOG.info(request + ": Automatically granting access to index 
table during creation of view:"
+                + requestTable + authString(userName, dependentTable, 
requireAccess));
+        grantPermissions(userName, dependentTable.getName(), 
unionSet.toArray(new Action[0]));
     }
     
     private void grantPermissions(final String toUser, final byte[] table, 
final Action... actions) throws IOException {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/88038a2d/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java 
b/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
index e51fd9f..2301c32 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java
@@ -229,6 +229,7 @@ public enum SQLExceptionCode {
             return new TableAlreadyExistsException(info.getSchemaName(), 
info.getTableName());
         }
     }),
+    TABLES_NOT_IN_SYNC(1140, "42M05", "Tables not in sync for some 
properties."),
 
     // Syntax error
     TYPE_NOT_SUPPORTED_FOR_OPERATOR(1014, "42Y01", "The operator does not 
support the operand type."),

http://git-wip-us.apache.org/repos/asf/phoenix/blob/88038a2d/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java 
b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
index 174e643..384c8cc 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java
@@ -97,6 +97,7 @@ import org.apache.phoenix.parse.AliasedNode;
 import org.apache.phoenix.parse.AlterIndexStatement;
 import org.apache.phoenix.parse.AlterSessionStatement;
 import org.apache.phoenix.parse.BindableStatement;
+import org.apache.phoenix.parse.ChangePermsStatement;
 import org.apache.phoenix.parse.CloseStatement;
 import org.apache.phoenix.parse.ColumnDef;
 import org.apache.phoenix.parse.ColumnName;
@@ -212,8 +213,9 @@ public class PhoenixStatement implements Statement, 
SQLCloseable {
         QUERY("queried", false),
         DELETE("deleted", true),
         UPSERT("upserted", true),
-        UPGRADE("upgrade", true);
-        
+        UPGRADE("upgrade", true),
+        ADMIN("admin", true);
+
         private final String toString;
         private final boolean isMutation;
         Operation(String toString, boolean isMutation) {
@@ -1153,6 +1155,33 @@ public class PhoenixStatement implements Statement, 
SQLCloseable {
         }
     }
 
+    private static class ExecutableChangePermsStatement extends 
ChangePermsStatement implements CompilableStatement {
+
+        public ExecutableChangePermsStatement (String permsString, boolean 
isSchemaName, TableName tableName,
+                                               String schemaName, boolean 
isGroupName, LiteralParseNode userOrGroup, boolean isGrantStatement) {
+            super(permsString, isSchemaName, tableName, schemaName, 
isGroupName, userOrGroup, isGrantStatement);
+        }
+
+        @Override
+        public MutationPlan compilePlan(PhoenixStatement stmt, 
Sequence.ValueOp seqAction) throws SQLException {
+            final StatementContext context = new StatementContext(stmt);
+
+            return new BaseMutationPlan(context, this.getOperation()) {
+
+                @Override
+                public ExplainPlan getExplainPlan() throws SQLException {
+                    return new ExplainPlan(Collections.singletonList("GRANT 
PERMISSION"));
+                }
+
+                @Override
+                public MutationState execute() throws SQLException {
+                    MetaDataClient client = new 
MetaDataClient(getContext().getConnection());
+                    return 
client.changePermissions(ExecutableChangePermsStatement.this);
+                }
+            };
+        }
+    }
+
     private static class ExecutableDropIndexStatement extends 
DropIndexStatement implements CompilableStatement {
 
         public ExecutableDropIndexStatement(NamedNode indexName, TableName 
tableName, boolean ifExists) {
@@ -1558,6 +1587,13 @@ public class PhoenixStatement implements Statement, 
SQLCloseable {
         public ExecuteUpgradeStatement executeUpgrade() {
             return new ExecutableExecuteUpgradeStatement();
         }
+
+        @Override
+        public ExecutableChangePermsStatement changePermsStatement(String 
permsString, boolean isSchemaName, TableName tableName,
+                                                         String schemaName, 
boolean isGroupName, LiteralParseNode userOrGroup, boolean isGrantStatement) {
+            return new ExecutableChangePermsStatement(permsString, 
isSchemaName, tableName, schemaName, isGroupName, userOrGroup,isGrantStatement);
+        }
+
     }
     
     static class PhoenixStatementParser extends SQLParser {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/88038a2d/phoenix-core/src/main/java/org/apache/phoenix/parse/ChangePermsStatement.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/parse/ChangePermsStatement.java 
b/phoenix-core/src/main/java/org/apache/phoenix/parse/ChangePermsStatement.java
new file mode 100644
index 0000000..0eae26f
--- /dev/null
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/parse/ChangePermsStatement.java
@@ -0,0 +1,102 @@
+/*
+ * 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.phoenix.parse;
+
+import org.antlr.runtime.RecognitionException;
+import org.apache.hadoop.hbase.AuthUtil;
+import org.apache.hadoop.hbase.security.access.Permission;
+import org.apache.phoenix.exception.PhoenixParserException;
+import org.apache.phoenix.jdbc.PhoenixStatement;
+import org.apache.phoenix.util.SchemaUtil;
+
+import java.util.Arrays;
+
+/**
+ * See PHOENIX-672, Use GRANT/REVOKE statements to assign or remove 
permissions for a user OR group on a table OR namespace
+ * Permissions are managed by HBase using hbase:acl table, Allowed permissions 
are RWXCA
+ */
+public class ChangePermsStatement implements BindableStatement {
+
+    private Permission.Action[] permsList;
+    private TableName tableName;
+    private String schemaName;
+    private String name;
+    // Grant/Revoke statements are differentiated based on this boolean
+    private boolean isGrantStatement;
+
+    public ChangePermsStatement(String permsString, boolean isSchemaName,
+                                TableName tableName, String schemaName, 
boolean isGroupName, LiteralParseNode ugNode, boolean isGrantStatement) {
+        // PHOENIX-672 HBase API doesn't allow to revoke specific permissions, 
hence this parameter will be ignored here.
+        // To comply with SQL standards, we may support the user given 
permissions to revoke specific permissions in future.
+        // GRANT permissions statement requires this parameter and the parsing 
will fail if it is not specified in SQL
+        if(permsString != null) {
+            Permission permission = new Permission(permsString.getBytes());
+            permsList = permission.getActions();
+        }
+        if(isSchemaName) {
+            this.schemaName = SchemaUtil.normalizeIdentifier(schemaName);
+        } else {
+            this.tableName = tableName;
+        }
+        name = SchemaUtil.normalizeLiteral(ugNode);
+        name = isGroupName ? AuthUtil.toGroupEntry(name) : name;
+        this.isGrantStatement = isGrantStatement;
+    }
+
+    public Permission.Action[] getPermsList() {
+        return permsList;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public TableName getTableName() {
+        return tableName;
+    }
+
+    public String getSchemaName() {
+        return schemaName;
+    }
+
+    public boolean isGrantStatement() {
+        return isGrantStatement;
+    }
+
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+        buffer = this.isGrantStatement() ? buffer.append("GRANT ") : 
buffer.append("REVOKE ");
+        buffer.append("permissions requested for user/group: " + 
this.getName());
+        if (this.getSchemaName() != null) {
+            buffer.append(" for Schema: " + this.getSchemaName());
+        } else if (this.getTableName() != null) {
+            buffer.append(" for Table: " + this.getTableName());
+        }
+        buffer.append(" Permissions: " + Arrays.toString(this.getPermsList()));
+        return buffer.toString();
+    }
+
+    @Override
+    public int getBindCount() {
+        return 0;
+    }
+
+    @Override
+    public PhoenixStatement.Operation getOperation() {
+        return PhoenixStatement.Operation.ADMIN;
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/88038a2d/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java 
b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
index 0058f38..32c3d8d 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
@@ -25,7 +25,6 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Collection;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import com.google.common.collect.ArrayListMultimap;
@@ -925,4 +924,10 @@ public class ParseNodeFactory {
     public UseSchemaStatement useSchema(String schemaName) {
         return new UseSchemaStatement(schemaName);
     }
+
+    public ChangePermsStatement changePermsStatement(String permsString, 
boolean isSchemaName, TableName tableName
+            , String schemaName, boolean isGroupName, LiteralParseNode 
userOrGroup, boolean isGrantStatement) {
+        return new ChangePermsStatement(permsString, isSchemaName, tableName, 
schemaName, isGroupName, userOrGroup, isGrantStatement);
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/88038a2d/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
index 7a255a1..08aadfb 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
@@ -103,6 +103,7 @@ import org.apache.hadoop.hbase.HConstants;
 import org.apache.hadoop.hbase.HRegionLocation;
 import org.apache.hadoop.hbase.HTableDescriptor;
 import org.apache.hadoop.hbase.NamespaceDescriptor;
+import org.apache.hadoop.hbase.NamespaceNotFoundException;
 import org.apache.hadoop.hbase.TableExistsException;
 import org.apache.hadoop.hbase.TableName;
 import org.apache.hadoop.hbase.client.Append;
@@ -190,6 +191,7 @@ import 
org.apache.phoenix.schema.EmptySequenceCacheException;
 import org.apache.phoenix.schema.FunctionNotFoundException;
 import org.apache.phoenix.schema.MetaDataClient;
 import org.apache.phoenix.schema.MetaDataSplitPolicy;
+import org.apache.phoenix.schema.NewerSchemaAlreadyExistsException;
 import org.apache.phoenix.schema.NewerTableAlreadyExistsException;
 import org.apache.phoenix.schema.PColumn;
 import org.apache.phoenix.schema.PColumnFamily;
@@ -1224,7 +1226,7 @@ public class ConnectionQueryServicesImpl extends 
DelegateQueryServices implement
                             
SQLExceptionCode.INCONSISTENT_NAMESPACE_MAPPING_PROPERTIES)
                     .setMessage(
                             "Ensure that config " + 
QueryServices.IS_NAMESPACE_MAPPING_ENABLED
-                            + " is consitent on client and server.")
+                            + " is consistent on client and server.")
                             .build().buildException(); }
             lowestClusterHBaseVersion = minHBaseVersion;
         } catch (SQLException e) {
@@ -2460,6 +2462,11 @@ public class ConnectionQueryServicesImpl extends 
DelegateQueryServices implement
                                         logger.warn("Could not check for 
Phoenix SYSTEM tables, assuming they exist and are properly configured");
                                         
checkClientServerCompatibility(SchemaUtil.getPhysicalName(SYSTEM_CATALOG_NAME_BYTES,
 getProps()).getName());
                                         success = true;
+                                    } else if 
(!Iterables.isEmpty(Iterables.filter(Throwables.getCausalChain(e), 
NamespaceNotFoundException.class))) {
+                                        // This exception is only possible if 
SYSTEM namespace mapping is enabled and SYSTEM namespace is missing
+                                        // It implies that SYSTEM tables are 
not created and hence we shouldn't provide a connection
+                                        AccessDeniedException ade = new 
AccessDeniedException("Insufficient permissions to create SYSTEM namespace and 
SYSTEM Tables");
+                                        initializationException = 
ServerUtil.parseServerException(ade);
                                     } else {
                                         initializationException = e;
                                     }
@@ -2471,8 +2478,19 @@ public class ConnectionQueryServicesImpl extends 
DelegateQueryServices implement
                                 // with SYSTEM Namespace. (See PHOENIX-4227 
https://issues.apache.org/jira/browse/PHOENIX-4227)
                                 if 
(SchemaUtil.isNamespaceMappingEnabled(PTableType.SYSTEM,
                                         
ConnectionQueryServicesImpl.this.getProps())) {
-                                    
metaConnection.createStatement().execute("CREATE SCHEMA IF NOT EXISTS "
-                                            + 
PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA);
+                                    try {
+                                        
metaConnection.createStatement().execute("CREATE SCHEMA IF NOT EXISTS "
+                                                + 
PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA);
+                                    } catch (NewerSchemaAlreadyExistsException 
e) {
+                                        // Older clients with appropriate 
perms may try getting a new connection
+                                        // This results in 
NewerSchemaAlreadyExistsException, so we can safely ignore it here
+                                    } catch (PhoenixIOException e) {
+                                        if 
(!Iterables.isEmpty(Iterables.filter(Throwables.getCausalChain(e), 
AccessDeniedException.class))) {
+                                            // Ignore ADE
+                                        } else {
+                                            throw e;
+                                        }
+                                    }
                                 }
                                 if 
(!ConnectionQueryServicesImpl.this.upgradeRequired.get()) {
                                     createOtherSystemTables(metaConnection, 
hBaseAdmin);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/88038a2d/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java 
b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java
index 7607388..851ba9a 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java
@@ -149,6 +149,7 @@ public interface QueryConstants {
     public enum JoinType {INNER, LEFT_OUTER}
     public final static String SYSTEM_SCHEMA_NAME = "SYSTEM";
     public final static byte[] SYSTEM_SCHEMA_NAME_BYTES = 
Bytes.toBytes(SYSTEM_SCHEMA_NAME);
+    public final static String HBASE_DEFAULT_SCHEMA_NAME = "default";
     public final static String PHOENIX_METADATA = "table";
     public final static String OFFSET_ROW_KEY = "_OFFSET_";
     public final static byte[] OFFSET_ROW_KEY_BYTES = 
Bytes.toBytes(OFFSET_ROW_KEY);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/88038a2d/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java 
b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
index b9ed734..59f7385 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
@@ -262,8 +262,6 @@ public interface QueryServices extends SQLCloseable {
     public static final String UPLOAD_BINARY_DATA_TYPE_ENCODING = 
"phoenix.upload.binaryDataType.encoding";
     // Toggle for server-written updates to SYSTEM.CATALOG
     public static final String PHOENIX_ACLS_ENABLED = "phoenix.acls.enabled";
-    public static final String PHOENIX_AUTOMATIC_GRANT_ENABLED = 
"phoenix.security.automatic.grant.enabled";
-    public static final String PHOENIX_SECURITY_PERMISSION_STRICT_MODE_ENABLED 
= "phoenix.security.strict.mode.enabled";
 
     public static final String INDEX_ASYNC_BUILD_ENABLED = 
"phoenix.index.async.build.enabled";
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/88038a2d/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java 
b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
index a586c28..3ceb084 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
@@ -59,13 +59,11 @@ import static 
org.apache.phoenix.query.QueryServices.MIN_STATS_UPDATE_FREQ_MS_AT
 import static org.apache.phoenix.query.QueryServices.MUTATE_BATCH_SIZE_ATTRIB;
 import static 
org.apache.phoenix.query.QueryServices.NUM_RETRIES_FOR_SCHEMA_UPDATE_CHECK;
 import static org.apache.phoenix.query.QueryServices.PHOENIX_ACLS_ENABLED;
-import static 
org.apache.phoenix.query.QueryServices.PHOENIX_AUTOMATIC_GRANT_ENABLED;
 import static 
org.apache.phoenix.query.QueryServices.PHOENIX_QUERY_SERVER_CLUSTER_BASE_PATH;
 import static 
org.apache.phoenix.query.QueryServices.PHOENIX_QUERY_SERVER_LOADBALANCER_ENABLED;
 import static 
org.apache.phoenix.query.QueryServices.PHOENIX_QUERY_SERVER_SERVICE_NAME;
 import static 
org.apache.phoenix.query.QueryServices.PHOENIX_QUERY_SERVER_ZK_ACL_PASSWORD;
 import static 
org.apache.phoenix.query.QueryServices.PHOENIX_QUERY_SERVER_ZK_ACL_USERNAME;
-import static 
org.apache.phoenix.query.QueryServices.PHOENIX_SECURITY_PERMISSION_STRICT_MODE_ENABLED;
 import static org.apache.phoenix.query.QueryServices.QUEUE_SIZE_ATTRIB;
 import static 
org.apache.phoenix.query.QueryServices.REGIONSERVER_INFO_PORT_ATTRIB;
 import static org.apache.phoenix.query.QueryServices.RENEW_LEASE_ENABLED;
@@ -322,8 +320,6 @@ public class QueryServicesOptions {
     
     //Security defaults
     public static final boolean DEFAULT_PHOENIX_ACLS_ENABLED = false;
-    public static final boolean DEFAULT_PHOENIX_AUTOMATIC_GRANT_ENABLED = 
false;
-    public static final boolean 
DEFAULT_PHOENIX_SECURITY_PERMISSION_STRICT_MODE_ENABLED = true;
 
     //default update cache frequency
     public static final int DEFAULT_UPDATE_CACHE_FREQUENCY = 0;
@@ -423,9 +419,7 @@ public class QueryServicesOptions {
             .setIfUnset(STATS_COLLECTION_ENABLED, 
DEFAULT_STATS_COLLECTION_ENABLED)
             .setIfUnset(USE_STATS_FOR_PARALLELIZATION, 
DEFAULT_USE_STATS_FOR_PARALLELIZATION)
             .setIfUnset(UPLOAD_BINARY_DATA_TYPE_ENCODING, 
DEFAULT_UPLOAD_BINARY_DATA_TYPE_ENCODING)
-            .setIfUnset(PHOENIX_ACLS_ENABLED,  DEFAULT_PHOENIX_ACLS_ENABLED)
-            .setIfUnset(PHOENIX_AUTOMATIC_GRANT_ENABLED,  
DEFAULT_PHOENIX_AUTOMATIC_GRANT_ENABLED)
-            .setIfUnset(PHOENIX_SECURITY_PERMISSION_STRICT_MODE_ENABLED,  
DEFAULT_PHOENIX_SECURITY_PERMISSION_STRICT_MODE_ENABLED);
+            .setIfUnset(PHOENIX_ACLS_ENABLED,  DEFAULT_PHOENIX_ACLS_ENABLED);
         // HBase sets this to 1, so we reset it to something more appropriate.
         // Hopefully HBase will change this, because we can't know if a user 
set
         // it to 1, so we'll change it.

http://git-wip-us.apache.org/repos/asf/phoenix/blob/88038a2d/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
index 338b325..1f76e90 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
@@ -113,6 +113,7 @@ import java.sql.SQLException;
 import java.sql.SQLFeatureNotSupportedException;
 import java.sql.Types;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Collection;
 import java.util.Collections;
@@ -130,11 +131,16 @@ import java.util.Set;
 import org.apache.hadoop.hbase.HColumnDescriptor;
 import org.apache.hadoop.hbase.HConstants;
 import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.client.ClusterConnection;
 import org.apache.hadoop.hbase.client.Delete;
 import org.apache.hadoop.hbase.client.HBaseAdmin;
+import org.apache.hadoop.hbase.client.HConnection;
 import org.apache.hadoop.hbase.client.Mutation;
 import org.apache.hadoop.hbase.client.Put;
 import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.security.AccessDeniedException;
+import org.apache.hadoop.hbase.security.access.AccessControlClient;
+import org.apache.hadoop.hbase.security.access.Permission;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.hbase.util.Pair;
 import org.apache.phoenix.compile.ColumnResolver;
@@ -165,6 +171,7 @@ import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
 import org.apache.phoenix.jdbc.PhoenixStatement;
 import org.apache.phoenix.parse.AddColumnStatement;
 import org.apache.phoenix.parse.AlterIndexStatement;
+import org.apache.phoenix.parse.ChangePermsStatement;
 import org.apache.phoenix.parse.CloseStatement;
 import org.apache.phoenix.parse.ColumnDef;
 import org.apache.phoenix.parse.ColumnDefInPkConstraint;
@@ -229,6 +236,7 @@ import org.apache.phoenix.util.QueryUtil;
 import org.apache.phoenix.util.ReadOnlyProps;
 import org.apache.phoenix.util.ScanUtil;
 import org.apache.phoenix.util.SchemaUtil;
+import org.apache.phoenix.util.ServerUtil;
 import org.apache.phoenix.util.StringUtil;
 import org.apache.phoenix.util.TransactionUtil;
 import org.apache.phoenix.util.UpgradeUtil;
@@ -4168,4 +4176,134 @@ public class MetaDataClient {
         }
         return new MutationState(0, 0, connection);
     }
+
+    /**
+     * GRANT/REVOKE statements use this method to update HBase acl's
+     * Perms can be changed at Schema, Table or User level
+     * @throws SQLException
+     */
+    public MutationState changePermissions(ChangePermsStatement 
changePermsStatement) throws SQLException {
+
+        logger.info(changePermsStatement.toString());
+
+        try(HBaseAdmin admin = connection.getQueryServices().getAdmin()) {
+            ClusterConnection clusterConnection = (ClusterConnection) 
admin.getConnection();
+
+            if (changePermsStatement.getSchemaName() != null) {
+                // SYSTEM.CATALOG doesn't have any entry for "default" HBase 
namespace, hence we will bypass the check
+                
if(!changePermsStatement.getSchemaName().equals(QueryConstants.HBASE_DEFAULT_SCHEMA_NAME))
 {
+                    
FromCompiler.getResolverForSchema(changePermsStatement.getSchemaName(), 
connection);
+                }
+
+                changePermsOnSchema(clusterConnection, changePermsStatement);
+            } else if (changePermsStatement.getTableName() != null) {
+                PTable inputTable = PhoenixRuntime.getTable(connection,
+                        
SchemaUtil.normalizeFullTableName(changePermsStatement.getTableName().toString()));
+                if (!(PTableType.TABLE.equals(inputTable.getType()) || 
PTableType.SYSTEM.equals(inputTable.getType()))) {
+                    throw new AccessDeniedException("Cannot GRANT or REVOKE 
permissions on INDEX TABLES or VIEWS");
+                }
+
+                // Changing perms on base table and update the perms for 
global and view indexes
+                // Views and local indexes are not physical tables and hence 
update perms is not needed
+                changePermsOnTables(clusterConnection, admin, 
changePermsStatement, inputTable);
+            } else {
+
+                // User can be given perms at the global level
+                changePermsOnUser(clusterConnection, changePermsStatement);
+            }
+
+        } catch (SQLException e) {
+            // Bubble up the SQL Exception
+            throw e;
+        } catch (Throwable throwable) {
+            // To change perms, the user must have ADMIN perms on that scope, 
otherwise it throws ADE
+            // Wrap around ADE and other exceptions to PhoenixIOException
+            throw ServerUtil.parseServerException(throwable);
+        }
+
+        return new MutationState(0, 0, connection);
+    }
+
+    private void changePermsOnSchema(ClusterConnection clusterConnection, 
ChangePermsStatement changePermsStatement) throws Throwable {
+        if(changePermsStatement.isGrantStatement()) {
+            AccessControlClient.grant(clusterConnection, 
changePermsStatement.getSchemaName(), changePermsStatement.getName(), 
changePermsStatement.getPermsList());
+        } else {
+            AccessControlClient.revoke(clusterConnection, 
changePermsStatement.getSchemaName(), changePermsStatement.getName(), 
Permission.Action.values());
+        }
+    }
+
+    private void changePermsOnTables(ClusterConnection clusterConnection, 
HBaseAdmin admin, ChangePermsStatement changePermsStatement, PTable inputTable) 
throws Throwable {
+
+        org.apache.hadoop.hbase.TableName tableName = 
SchemaUtil.getPhysicalTableName
+                (inputTable.getPhysicalName().getBytes(), 
inputTable.isNamespaceMapped());
+
+        changePermsOnTable(clusterConnection, changePermsStatement, tableName);
+
+        boolean schemaInconsistency = false;
+        List<PTable> inconsistentTables = null;
+
+        for(PTable indexTable : inputTable.getIndexes()) {
+            // Local Indexes don't correspond to new physical table, they are 
just stored in separate CF of base table.
+            if(indexTable.getIndexType().equals(IndexType.LOCAL)) {
+                continue;
+            }
+            if (inputTable.isNamespaceMapped() != 
indexTable.isNamespaceMapped()) {
+                schemaInconsistency = true;
+                if(inconsistentTables == null) {
+                    inconsistentTables = new ArrayList<>();
+                }
+                inconsistentTables.add(indexTable);
+                continue;
+            }
+            logger.info("Updating permissions for Index Table: " +
+                    indexTable.getName() + " Base Table: " + 
inputTable.getName());
+            tableName = 
SchemaUtil.getPhysicalTableName(indexTable.getPhysicalName().getBytes(), 
indexTable.isNamespaceMapped());
+            changePermsOnTable(clusterConnection, changePermsStatement, 
tableName);
+        }
+
+        if(schemaInconsistency) {
+            for(PTable table : inconsistentTables) {
+                logger.error("Fail to propagate permissions to Index Table: " 
+ table.getName());
+            }
+            throw new 
TablesNotInSyncException(inputTable.getTableName().getString(),
+                    inconsistentTables.get(0).getTableName().getString(), 
"Namespace properties");
+        }
+
+        // There will be only a single View Index Table for all the indexes 
created on views
+        byte[] viewIndexTableBytes = 
MetaDataUtil.getViewIndexPhysicalName(inputTable.getPhysicalName().getBytes());
+        tableName = 
org.apache.hadoop.hbase.TableName.valueOf(viewIndexTableBytes);
+        boolean viewIndexTableExists = admin.tableExists(tableName);
+        if(viewIndexTableExists) {
+            logger.info("Updating permissions for View Index Table: " +
+                    Bytes.toString(viewIndexTableBytes) + " Base Table: " + 
inputTable.getName());
+            changePermsOnTable(clusterConnection, changePermsStatement, 
tableName);
+        } else {
+            if(inputTable.isMultiTenant()) {
+                logger.error("View Index Table not found for MultiTenant 
Table: " + inputTable.getName());
+                logger.error("Fail to propagate permissions to view Index 
Table: " + tableName.getNameAsString());
+                throw new 
TablesNotInSyncException(inputTable.getTableName().getString(),
+                        Bytes.toString(viewIndexTableBytes), " View Index 
table should exist for MultiTenant tables");
+            }
+        }
+    }
+
+    private void changePermsOnTable(ClusterConnection clusterConnection, 
ChangePermsStatement changePermsStatement, org.apache.hadoop.hbase.TableName 
tableName)
+            throws Throwable {
+        if(changePermsStatement.isGrantStatement()) {
+            AccessControlClient.grant(clusterConnection, tableName, 
changePermsStatement.getName(),
+                    null, null, changePermsStatement.getPermsList());
+        } else {
+            AccessControlClient.revoke(clusterConnection, tableName, 
changePermsStatement.getName(),
+                    null, null, Permission.Action.values());
+        }
+    }
+
+    private void changePermsOnUser(ClusterConnection clusterConnection, 
ChangePermsStatement changePermsStatement)
+            throws Throwable {
+        if(changePermsStatement.isGrantStatement()) {
+            AccessControlClient.grant(clusterConnection, 
changePermsStatement.getName(), changePermsStatement.getPermsList());
+        } else {
+            AccessControlClient.revoke(clusterConnection, 
changePermsStatement.getName(), Permission.Action.values());
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/88038a2d/phoenix-core/src/main/java/org/apache/phoenix/schema/TablesNotInSyncException.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/TablesNotInSyncException.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/TablesNotInSyncException.java
new file mode 100644
index 0000000..e58df71
--- /dev/null
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/TablesNotInSyncException.java
@@ -0,0 +1,22 @@
+package org.apache.phoenix.schema;
+
+import org.apache.phoenix.exception.SQLExceptionCode;
+import org.apache.phoenix.exception.SQLExceptionInfo;
+
+import java.sql.SQLException;
+
+/**
+ * Exception to raise when multiple tables differ in specified properties
+ * This can happen since Apache Phoenix code doesn't work atomically for many 
parts
+ * For example, Base table and index tables are inconsistent in namespace 
mapping
+ * OR View Index table doesn't exist for multi-tenant base table
+ */
+public class TablesNotInSyncException extends SQLException {
+    private static final long serialVersionUID = 1L;
+    private static SQLExceptionCode code = SQLExceptionCode.TABLES_NOT_IN_SYNC;
+
+    public TablesNotInSyncException(String table1, String table2, String diff) 
{
+        super(new SQLExceptionInfo.Builder(code).setMessage("Table: " + table1 
+ " and Table: " + table2 + " differ in " + diff).build().toString(), 
code.getSQLState(), code.getErrorCode());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/88038a2d/phoenix-core/src/main/java/org/apache/phoenix/util/SchemaUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/SchemaUtil.java 
b/phoenix-core/src/main/java/org/apache/phoenix/util/SchemaUtil.java
index 47b4b43..5b5c3a5 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/SchemaUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/SchemaUtil.java
@@ -58,6 +58,7 @@ import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.hbase.index.util.ImmutableBytesPtr;
 import org.apache.phoenix.jdbc.PhoenixConnection;
 import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
+import org.apache.phoenix.parse.LiteralParseNode;
 import org.apache.phoenix.query.KeyRange;
 import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.query.QueryServices;
@@ -205,7 +206,25 @@ public class SchemaUtil {
         }
         return name.toUpperCase();
     }
-    
+
+    /**
+     * Normalize a Literal. If literal is surrounded by single quotes,
+     * the quotes are trimmed, else full string is returned
+     * @param literal the parsed LiteralParseNode
+     * @return the normalized literal string
+     */
+    public static String normalizeLiteral(LiteralParseNode literal) {
+        if (literal == null) {
+            return null;
+        }
+        String literalString = literal.toString();
+        if (isEnclosedInSingleQuotes(literalString)) {
+            // Trim the single quotes
+            return literalString.substring(1, literalString.length()-1);
+        }
+        return literalString;
+    }
+
     /**
      * Normalizes the fulltableName . Uses {@linkplain normalizeIdentifier}
      * @param fullTableName
@@ -221,6 +240,10 @@ public class SchemaUtil {
         return normalizedTableName + normalizeIdentifier(tableName);
     }
 
+    public static boolean isEnclosedInSingleQuotes(String name) {
+        return name!=null && name.length() > 0 && name.charAt(0)=='\'';
+    }
+
     public static boolean isCaseSensitive(String name) {
         return name!=null && name.length() > 0 && name.charAt(0)=='"';
     }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/88038a2d/phoenix-core/src/test/java/org/apache/phoenix/parse/QueryParserTest.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/test/java/org/apache/phoenix/parse/QueryParserTest.java 
b/phoenix-core/src/test/java/org/apache/phoenix/parse/QueryParserTest.java
index 431f60b..25f59c0 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/parse/QueryParserTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/parse/QueryParserTest.java
@@ -26,6 +26,8 @@ import java.io.IOException;
 import java.io.StringReader;
 import java.sql.SQLException;
 import java.sql.SQLFeatureNotSupportedException;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import org.apache.hadoop.hbase.util.Pair;
@@ -56,7 +58,7 @@ public class QueryParserTest {
         }
         assertEquals("Expected equality:\n" + sql + "\n" + newSQL, stmt, 
newStmt);
     }
-    
+
     private void parseQueryThatShouldFail(String sql) throws Exception {
         try {
             parseQuery(sql);
@@ -67,6 +69,48 @@ public class QueryParserTest {
     }
 
     @Test
+    public void testParseGrantQuery() throws Exception {
+
+        String sql0 = "GRANT 'RX' ON SYSTEM.\"SEQUENCE\" TO 'user'";
+        parseQuery(sql0);
+        String sql1 = "GRANT 'RWXCA' ON TABLE some_table0 TO 'user0'";
+        parseQuery(sql1);
+        String sql2 = "GRANT 'RWX' ON some_table1 TO 'user1'";
+        parseQuery(sql2);
+        String sql3 = "GRANT 'CA' ON SCHEMA some_schema2 TO 'user2'";
+        parseQuery(sql3);
+        String sql4 = "GRANT 'RXW' ON some_table3 TO GROUP 'group3'";
+        parseQuery(sql4);
+        String sql5 = "GRANT 'RXW' ON \"some_schema5\".\"some_table5\" TO 
GROUP 'group5'";
+        parseQuery(sql5);
+        String sql6 = "GRANT 'RWA' TO 'user6'";
+        parseQuery(sql6);
+        String sql7 = "GRANT 'A' TO GROUP 'group7'";
+        parseQuery(sql7);
+        String sql8 = "GRANT 'ARXRRRRR' TO GROUP 'group8'";
+        parseQueryThatShouldFail(sql8);
+    }
+
+    @Test
+    public void testParseRevokeQuery() throws Exception {
+
+        String sql0 = "REVOKE ON SCHEMA SYSTEM FROM 'user0'";
+        parseQuery(sql0);
+        String sql1 = "REVOKE ON SYSTEM.\"SEQUENCE\" FROM 'user1'";
+        parseQuery(sql1);
+        String sql2 = "REVOKE ON TABLE some_table2 FROM GROUP 'group2'";
+        parseQuery(sql2);
+        String sql3 = "REVOKE ON some_table3 FROM GROUP 'group2'";
+        parseQuery(sql3);
+        String sql4 = "REVOKE FROM 'user4'";
+        parseQuery(sql4);
+        String sql5 = "REVOKE FROM GROUP 'group5'";
+        parseQuery(sql5);
+        String sql6 = "REVOKE 'RRWWXAAA' FROM GROUP 'group6'";
+        parseQueryThatShouldFail(sql6);
+    }
+
+    @Test
     public void testParsePreQuery0() throws Exception {
         String sql = ((
             "select a from b\n" +

Reply via email to