You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by xe...@apache.org on 2012/09/10 17:31:37 UTC

git commit: Improve IAuthority interface by introducing fine-grained access permissions and grant/revoke commands patch by Pavel Yaskevich; reviewed by Yuki Morishita for CASSANDRA-4490

Updated Branches:
  refs/heads/cassandra-1.1 e39530789 -> aba5a3765


Improve IAuthority interface by introducing fine-grained access permissions and grant/revoke commands
patch by Pavel Yaskevich; reviewed by Yuki Morishita for CASSANDRA-4490


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/aba5a376
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/aba5a376
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/aba5a376

Branch: refs/heads/cassandra-1.1
Commit: aba5a37650232dbf10b505c04b257f73b6c9b579
Parents: e395307
Author: Pavel Yaskevich <xe...@apache.org>
Authored: Mon Sep 10 18:27:26 2012 +0300
Committer: Pavel Yaskevich <xe...@apache.org>
Committed: Mon Sep 10 18:27:26 2012 +0300

----------------------------------------------------------------------
 CHANGES.txt                                        |    5 +
 .../apache/cassandra/auth/AllowAllAuthority.java   |    2 +-
 src/java/org/apache/cassandra/auth/IAuthority.java |    1 +
 .../org/apache/cassandra/auth/IAuthority2.java     |   69 +++++++++++
 .../apache/cassandra/auth/IAuthorityContainer.java |   82 +++++++++++++
 src/java/org/apache/cassandra/auth/Permission.java |   31 +++++-
 .../apache/cassandra/auth/PermissionDenied.java    |   34 +++++
 .../org/apache/cassandra/config/CFMetaData.java    |    2 +-
 .../cassandra/config/DatabaseDescriptor.java       |   16 ++-
 .../org/apache/cassandra/config/KSMetaData.java    |    7 +-
 .../org/apache/cassandra/cql/DeleteStatement.java  |    2 +-
 .../org/apache/cassandra/cql/QueryProcessor.java   |   21 ++--
 .../org/apache/cassandra/cql/UpdateStatement.java  |    2 +-
 src/java/org/apache/cassandra/cql3/CFName.java     |    5 +
 src/java/org/apache/cassandra/cql3/Cql.g           |   64 ++++++++++-
 .../org/apache/cassandra/cql3/QueryProcessor.java  |   39 ++++++
 .../cql3/statements/AlterTableStatement.java       |    7 +
 .../cassandra/cql3/statements/BatchStatement.java  |    2 +-
 .../statements/CreateColumnFamilyStatement.java    |    7 +
 .../cql3/statements/CreateIndexStatement.java      |    7 +
 .../cql3/statements/CreateKeyspaceStatement.java   |    6 +
 .../cql3/statements/DropColumnFamilyStatement.java |    8 ++
 .../cql3/statements/DropIndexStatement.java        |    7 +
 .../cql3/statements/DropKeyspaceStatement.java     |    8 +-
 .../cassandra/cql3/statements/GrantStatement.java  |   66 ++++++++++
 .../cql3/statements/ListGrantsStatement.java       |   53 ++++++++
 .../cql3/statements/ModificationStatement.java     |    2 +-
 .../cassandra/cql3/statements/RevokeStatement.java |   66 ++++++++++
 .../cql3/statements/SchemaAlteringStatement.java   |   16 ---
 .../cassandra/cql3/statements/SelectStatement.java |    2 +-
 .../cql3/statements/TruncateStatement.java         |    2 +-
 .../org/apache/cassandra/service/ClientState.java  |   95 +++++++++++----
 .../apache/cassandra/thrift/CassandraServer.java   |   49 ++++----
 33 files changed, 693 insertions(+), 92 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 61cede4..246bc49 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,8 @@
+1.1.6
+ * Improve IAuthority interface by introducing fine-grained
+   access permissions and grant/revoke commands (CASSANDRA-4490)
+
+
 1.1.5
  * add SecondaryIndex.reload API (CASSANDRA-4581)
  * use millis + atomicint for commitlog segment creation instead of

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/auth/AllowAllAuthority.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/AllowAllAuthority.java b/src/java/org/apache/cassandra/auth/AllowAllAuthority.java
index f41e00f..4ba9f77 100644
--- a/src/java/org/apache/cassandra/auth/AllowAllAuthority.java
+++ b/src/java/org/apache/cassandra/auth/AllowAllAuthority.java
@@ -36,4 +36,4 @@ public class AllowAllAuthority implements IAuthority
     {
         // pass
     }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/auth/IAuthority.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/IAuthority.java b/src/java/org/apache/cassandra/auth/IAuthority.java
index 09ae175..79c925e 100644
--- a/src/java/org/apache/cassandra/auth/IAuthority.java
+++ b/src/java/org/apache/cassandra/auth/IAuthority.java
@@ -27,6 +27,7 @@ import java.util.List;
 import org.apache.cassandra.config.ConfigurationException;
 
 /**
+ *
  * Cassandra's resource hierarchy looks something like:
  * {{/cassandra/keyspaces/$ks_name/...}}
  *

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/auth/IAuthority2.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/IAuthority2.java b/src/java/org/apache/cassandra/auth/IAuthority2.java
new file mode 100644
index 0000000..14f0e53
--- /dev/null
+++ b/src/java/org/apache/cassandra/auth/IAuthority2.java
@@ -0,0 +1,69 @@
+/**
+ * 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.cassandra.auth;
+
+import org.apache.cassandra.cql3.CFName;
+import org.apache.cassandra.thrift.CqlResult;
+import org.apache.cassandra.thrift.InvalidRequestException;
+
+public interface IAuthority2 extends IAuthority
+{
+    /**
+     * Setup is called each time upon system startup
+     */
+    public void setup();
+
+    /**
+     * GRANT <permission> ON <resource> TO <user> [WITH GRANT OPTION];
+     *
+     * @param granter The user who grants the permission
+     * @param permission The specific permission
+     * @param to Grantee of the permission
+     * @param resource The resource which is affect by permission change
+     * @param grantOption Does grantee has a permission to grant the same kind of permission on this particular resource?
+     *
+     * @throws InvalidRequestException upon parameter misconfiguration or internal error.
+     */
+    public void grant(AuthenticatedUser granter, Permission permission, String to, CFName resource, boolean grantOption) throws InvalidRequestException;
+
+    /**
+     * REVOKE <permission> ON <resource> FROM <user_name>;
+     *
+     * @param revoker The user know requests permission revoke
+     * @param permission The permission to revoke
+     * @param from The user to revoke permission from.
+     * @param resource The resource affected by permission change.
+     *
+     * @throws InvalidRequestException upon parameter misconfiguration or internal error.
+     */
+    public void revoke(AuthenticatedUser revoker, Permission permission, String from, CFName resource) throws InvalidRequestException;
+
+    /**
+     * LIST GRANTS FOR <user>;
+     * Not 'SHOW' because it's reserved for CQLsh for commands like 'show cluster'
+     *
+     * @param username The username to look for permissions.
+     *
+     * @return All of the permission of this particular user.
+     *
+     * @throws InvalidRequestException upon parameter misconfiguration or internal error.
+     */
+    public CqlResult listPermissions(String username) throws InvalidRequestException;
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/auth/IAuthorityContainer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/IAuthorityContainer.java b/src/java/org/apache/cassandra/auth/IAuthorityContainer.java
new file mode 100644
index 0000000..2279180
--- /dev/null
+++ b/src/java/org/apache/cassandra/auth/IAuthorityContainer.java
@@ -0,0 +1,82 @@
+/**
+ * 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.cassandra.auth;
+
+import org.apache.cassandra.cql3.CFName;
+import org.apache.cassandra.thrift.CqlResult;
+import org.apache.cassandra.thrift.InvalidRequestException;
+
+/**
+ * 1.1.x : Temporary measure to unable dynamic operations without changing IAuthority interface.
+ */
+public class IAuthorityContainer
+{
+    private final IAuthority authority;
+    private final IAuthority2 dynamicAuthority;
+
+    public IAuthorityContainer(IAuthority authority)
+    {
+        this.authority = authority;
+        dynamicAuthority = (authority instanceof IAuthority2) ? ((IAuthority2) authority) : null;
+    }
+
+    public void setup()
+    {
+        if (dynamicAuthority != null)
+            dynamicAuthority.setup();
+    }
+
+    public boolean isDynamic()
+    {
+        return dynamicAuthority != null;
+    }
+
+    public IAuthority getAuthority()
+    {
+        return authority;
+    }
+
+    public void grant(AuthenticatedUser granter, Permission permission, String to, CFName resource, boolean grantOption) throws InvalidRequestException
+    {
+        if (dynamicAuthority == null)
+            throw new InvalidRequestException("GRANT operation is not supported by your authority: " + authority);
+
+        if (permission.equals(Permission.READ) || permission.equals(Permission.WRITE))
+            throw new InvalidRequestException(String.format("Error setting permission to: %s, available permissions are %s", permission, Permission.GRANULAR_PERMISSIONS));
+
+        dynamicAuthority.grant(granter, permission, to, resource, grantOption);
+    }
+
+    public void revoke(AuthenticatedUser revoker, Permission permission, String from, CFName resource) throws InvalidRequestException
+    {
+        if (dynamicAuthority == null)
+            throw new InvalidRequestException("REVOKE operation is not supported by your authority: " + authority);
+
+        dynamicAuthority.revoke(revoker, permission, from, resource);
+    }
+
+    public CqlResult listPermissions(String username) throws InvalidRequestException
+    {
+        if (dynamicAuthority == null)
+            throw new InvalidRequestException("LIST GRANTS operation is not supported by your authority: " + authority);
+
+        return dynamicAuthority.listPermissions(username);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/auth/Permission.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/Permission.java b/src/java/org/apache/cassandra/auth/Permission.java
index 4e48539..7afb2ff 100644
--- a/src/java/org/apache/cassandra/auth/Permission.java
+++ b/src/java/org/apache/cassandra/auth/Permission.java
@@ -22,6 +22,8 @@
 package org.apache.cassandra.auth;
 
 import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * An enum encapsulating the set of possible permissions that an authenticated user can have for a resource.
@@ -30,9 +32,34 @@ import java.util.EnumSet;
  */
 public enum Permission
 {
-    READ,
-    WRITE;
+    READ,  // for backward compatibility
+    WRITE, // for backward compatibility
+
+    FULL_ACCESS,
+    NO_ACCESS,
+
+    // schema management
+    USE,
+    CREATE,
+    ALTER,
+    DROP,
+
+    // data access
+    UPDATE,
+    DELETE,
+    SELECT;
 
     public static final EnumSet<Permission> ALL = EnumSet.allOf(Permission.class);
     public static final EnumSet<Permission> NONE = EnumSet.noneOf(Permission.class);
+    public static final EnumSet<Permission> GRANULAR_PERMISSIONS = EnumSet.range(FULL_ACCESS, SELECT);
+
+    /**
+     * Maps old permissions to the new ones as we want to support old client IAuthority implementations
+     * and new style of granular permission checking at the same time.
+     */
+    public static final Map<Permission, EnumSet<Permission>> oldToNew = new HashMap<Permission, EnumSet<Permission>>(2)
+    {{
+        put(READ,  EnumSet.of(USE, SELECT));
+        put(WRITE, EnumSet.range(USE, DELETE));
+    }};
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/auth/PermissionDenied.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/PermissionDenied.java b/src/java/org/apache/cassandra/auth/PermissionDenied.java
new file mode 100644
index 0000000..22bf35a
--- /dev/null
+++ b/src/java/org/apache/cassandra/auth/PermissionDenied.java
@@ -0,0 +1,34 @@
+/**
+ * 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.cassandra.auth;
+
+import org.apache.cassandra.thrift.InvalidRequestException;
+
+public class PermissionDenied extends InvalidRequestException
+{
+    public PermissionDenied(String reason)
+    {
+        super(reason);
+    }
+
+    public String getMessage()
+    {
+        return why;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/config/CFMetaData.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/CFMetaData.java b/src/java/org/apache/cassandra/config/CFMetaData.java
index 9d77912..9e27231 100644
--- a/src/java/org/apache/cassandra/config/CFMetaData.java
+++ b/src/java/org/apache/cassandra/config/CFMetaData.java
@@ -226,7 +226,7 @@ public final class CFMetaData
     public CFMetaData columnAliases(List<ByteBuffer> prop) {columnAliases = prop; updateCfDef(); return this;}
     public CFMetaData valueAlias(ByteBuffer prop) {valueAlias = prop; updateCfDef(); return this;}
     public CFMetaData columnMetadata(Map<ByteBuffer,ColumnDefinition> prop) {column_metadata = prop; updateCfDef(); return this;}
-    private CFMetaData columnMetadata(ColumnDefinition... cds)
+    public CFMetaData columnMetadata(ColumnDefinition... cds)
     {
         Map<ByteBuffer, ColumnDefinition> map = new HashMap<ByteBuffer, ColumnDefinition>();
         for (ColumnDefinition cd : cds)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
index 54486d0..20fa981 100644
--- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
+++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
@@ -30,10 +30,7 @@ import java.util.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import org.apache.cassandra.auth.AllowAllAuthenticator;
-import org.apache.cassandra.auth.AllowAllAuthority;
-import org.apache.cassandra.auth.IAuthenticator;
-import org.apache.cassandra.auth.IAuthority;
+import org.apache.cassandra.auth.*;
 import org.apache.cassandra.cache.IRowCacheProvider;
 import org.apache.cassandra.config.Config.RequestSchedulerId;
 import org.apache.cassandra.db.ColumnFamilyStore;
@@ -75,6 +72,7 @@ public class DatabaseDescriptor
 
     private static IAuthenticator authenticator = new AllowAllAuthenticator();
     private static IAuthority authority = new AllowAllAuthority();
+    private static IAuthorityContainer authorityContainer;
 
     private final static String DEFAULT_CONFIGURATION = "cassandra.yaml";
 
@@ -206,6 +204,8 @@ public class DatabaseDescriptor
             authenticator.validateConfiguration();
             authority.validateConfiguration();
 
+            authorityContainer = new IAuthorityContainer(authority);
+
             /* Hashing strategy */
             if (conf.partitioner == null)
             {
@@ -452,6 +452,9 @@ public class DatabaseDescriptor
 
             Schema.instance.addSystemTable(systemMeta);
 
+            // setup schema required for authorization
+            authorityContainer.setup();
+
             /* Load the seeds for node contact points */
             if (conf.seed_provider == null)
             {
@@ -575,6 +578,11 @@ public class DatabaseDescriptor
         return authority;
     }
 
+    public static IAuthorityContainer getAuthorityContainer()
+    {
+        return authorityContainer;
+    }
+
     public static int getThriftMaxMessageLength()
     {
         return conf.thrift_max_message_length_in_mb * 1024 * 1024;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/config/KSMetaData.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/config/KSMetaData.java b/src/java/org/apache/cassandra/config/KSMetaData.java
index bd6c121..0aacc1c 100644
--- a/src/java/org/apache/cassandra/config/KSMetaData.java
+++ b/src/java/org/apache/cassandra/config/KSMetaData.java
@@ -66,7 +66,12 @@ public final class KSMetaData
         if (cls.equals(LocalStrategy.class))
             throw new ConfigurationException("Unable to use given strategy class: LocalStrategy is reserved for internal use.");
 
-        return new KSMetaData(name, cls, options, true, Collections.<CFMetaData>emptyList());
+        return newKeyspace(name, cls, options, Collections.<CFMetaData>emptyList());
+    }
+
+    public static KSMetaData newKeyspace(String name, Class<? extends AbstractReplicationStrategy> strategyClass, Map<String, String> options, Iterable<CFMetaData> cfDefs)
+    {
+        return new KSMetaData(name, strategyClass, options, true, cfDefs);
     }
 
     public static KSMetaData cloneWith(KSMetaData ksm, Iterable<CFMetaData> cfDefs)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/cql/DeleteStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql/DeleteStatement.java b/src/java/org/apache/cassandra/cql/DeleteStatement.java
index be59bb1..72fee13 100644
--- a/src/java/org/apache/cassandra/cql/DeleteStatement.java
+++ b/src/java/org/apache/cassandra/cql/DeleteStatement.java
@@ -73,7 +73,7 @@ public class DeleteStatement extends AbstractModification
     {
         CFMetaData metadata = validateColumnFamily(keyspace, columnFamily);
 
-        clientState.hasColumnFamilyAccess(columnFamily, Permission.WRITE);
+        clientState.hasColumnFamilyAccess(columnFamily, Permission.DELETE);
         AbstractType<?> keyType = Schema.instance.getCFMetaData(keyspace, columnFamily).getKeyValidator();
 
         List<IMutation> rowMutations = new ArrayList<IMutation>(keys.size());

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/cql/QueryProcessor.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql/QueryProcessor.java b/src/java/org/apache/cassandra/cql/QueryProcessor.java
index edb5388..4a829c6 100644
--- a/src/java/org/apache/cassandra/cql/QueryProcessor.java
+++ b/src/java/org/apache/cassandra/cql/QueryProcessor.java
@@ -249,7 +249,7 @@ public class QueryProcessor
             // Avoid unnecessary authorizations.
             if (!(cfamsSeen.contains(update.getColumnFamily())))
             {
-                clientState.hasColumnFamilyAccess(keyspace, update.getColumnFamily(), Permission.WRITE);
+                clientState.hasColumnFamilyAccess(keyspace, update.getColumnFamily(), Permission.UPDATE);
                 cfamsSeen.add(update.getColumnFamily());
             }
 
@@ -461,7 +461,7 @@ public class QueryProcessor
                 else
                     keyspace = oldKeyspace;
 
-                clientState.hasColumnFamilyAccess(keyspace, select.getColumnFamily(), Permission.READ);
+                clientState.hasColumnFamilyAccess(keyspace, select.getColumnFamily(), Permission.SELECT);
                 metadata = validateColumnFamily(keyspace, select.getColumnFamily());
 
                 // need to do this in here because we need a CFMD.getKeyName()
@@ -591,6 +591,7 @@ public class QueryProcessor
             case INSERT: // insert uses UpdateStatement
             case UPDATE:
                 UpdateStatement update = (UpdateStatement)statement.statement;
+                clientState.hasColumnFamilyAccess(keyspace, update.getColumnFamily(), Permission.UPDATE);
                 ThriftValidation.validateConsistencyLevel(keyspace, update.getConsistencyLevel(), RequestType.WRITE);
                 batchUpdate(clientState, Collections.singletonList(update), update.getConsistencyLevel(), variables);
                 result.type = CqlResultType.VOID;
@@ -647,7 +648,7 @@ public class QueryProcessor
                 keyspace = columnFamily.left == null ? clientState.getKeyspace() : columnFamily.left;
 
                 validateColumnFamily(keyspace, columnFamily.right);
-                clientState.hasColumnFamilyAccess(keyspace, columnFamily.right, Permission.WRITE);
+                clientState.hasColumnFamilyAccess(keyspace, columnFamily.right, Permission.DELETE);
 
                 try
                 {
@@ -669,6 +670,7 @@ public class QueryProcessor
                 DeleteStatement delete = (DeleteStatement)statement.statement;
 
                 keyspace = delete.keyspace == null ? clientState.getKeyspace() : delete.keyspace;
+                clientState.hasColumnFamilyAccess(keyspace, delete.columnFamily, Permission.DELETE);
                 List<IMutation> deletions = delete.prepareRowMutations(keyspace, clientState, variables);
                 for (IMutation deletion : deletions)
                 {
@@ -691,7 +693,7 @@ public class QueryProcessor
                 CreateKeyspaceStatement create = (CreateKeyspaceStatement)statement.statement;
                 create.validate();
                 ThriftValidation.validateKeyspaceNotSystem(create.getName());
-                clientState.hasKeyspaceSchemaAccess(Permission.WRITE);
+                clientState.hasKeyspaceAccess(create.getName(), Permission.CREATE);
                 validateSchemaAgreement();
 
                 try
@@ -715,7 +717,7 @@ public class QueryProcessor
 
             case CREATE_COLUMNFAMILY:
                 CreateColumnFamilyStatement createCf = (CreateColumnFamilyStatement)statement.statement;
-                clientState.hasColumnFamilySchemaAccess(Permission.WRITE);
+                clientState.hasColumnFamilySchemaAccess(createCf.getName(), Permission.CREATE);
                 validateSchemaAgreement();
 
                 try
@@ -735,7 +737,7 @@ public class QueryProcessor
 
             case CREATE_INDEX:
                 CreateIndexStatement createIdx = (CreateIndexStatement)statement.statement;
-                clientState.hasColumnFamilySchemaAccess(Permission.WRITE);
+                clientState.hasColumnFamilyAccess(keyspace, createIdx.getColumnFamily(), Permission.ALTER);
                 validateSchemaAgreement();
                 CFMetaData oldCfm = Schema.instance.getCFMetaData(keyspace, createIdx.getColumnFamily());
                 if (oldCfm == null)
@@ -780,7 +782,6 @@ public class QueryProcessor
 
             case DROP_INDEX:
                 DropIndexStatement dropIdx = (DropIndexStatement)statement.statement;
-                clientState.hasColumnFamilySchemaAccess(Permission.WRITE);
                 validateSchemaAgreement();
 
                 try
@@ -807,7 +808,7 @@ public class QueryProcessor
             case DROP_KEYSPACE:
                 String deleteKeyspace = (String)statement.statement;
                 ThriftValidation.validateKeyspaceNotSystem(deleteKeyspace);
-                clientState.hasKeyspaceSchemaAccess(Permission.WRITE);
+                clientState.hasKeyspaceAccess(deleteKeyspace, Permission.DROP);
                 validateSchemaAgreement();
 
                 try
@@ -827,7 +828,7 @@ public class QueryProcessor
 
             case DROP_COLUMNFAMILY:
                 String deleteColumnFamily = (String)statement.statement;
-                clientState.hasColumnFamilySchemaAccess(Permission.WRITE);
+                clientState.hasColumnFamilyAccess(keyspace, deleteColumnFamily, Permission.DROP);
                 validateSchemaAgreement();
 
                 try
@@ -849,7 +850,7 @@ public class QueryProcessor
                 AlterTableStatement alterTable = (AlterTableStatement) statement.statement;
 
                 validateColumnFamily(keyspace, alterTable.columnFamily);
-                clientState.hasColumnFamilyAccess(alterTable.columnFamily, Permission.WRITE);
+                clientState.hasColumnFamilyAccess(alterTable.columnFamily, Permission.ALTER);
                 validateSchemaAgreement();
 
                 try

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/cql/UpdateStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql/UpdateStatement.java b/src/java/org/apache/cassandra/cql/UpdateStatement.java
index 4c2be4d..b444da3 100644
--- a/src/java/org/apache/cassandra/cql/UpdateStatement.java
+++ b/src/java/org/apache/cassandra/cql/UpdateStatement.java
@@ -155,7 +155,7 @@ public class UpdateStatement extends AbstractModification
         // Avoid unnecessary authorizations.
         if (!(cfamsSeen.contains(columnFamily)))
         {
-            clientState.hasColumnFamilyAccess(columnFamily, Permission.WRITE);
+            clientState.hasColumnFamilyAccess(columnFamily, Permission.UPDATE);
             cfamsSeen.add(columnFamily);
         }
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/cql3/CFName.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/CFName.java b/src/java/org/apache/cassandra/cql3/CFName.java
index cd6e6d3..f48472c 100644
--- a/src/java/org/apache/cassandra/cql3/CFName.java
+++ b/src/java/org/apache/cassandra/cql3/CFName.java
@@ -50,6 +50,11 @@ public class CFName
         return cfName;
     }
 
+    public String toResource()
+    {
+        return "/cassandra/keyspaces/" + (hasKeyspace() ? ksName + "/" + cfName : cfName);
+    }
+
     @Override
     public String toString()
     {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/cql3/Cql.g
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Cql.g b/src/java/org/apache/cassandra/cql3/Cql.g
index 1900538..f0481f6 100644
--- a/src/java/org/apache/cassandra/cql3/Cql.g
+++ b/src/java/org/apache/cassandra/cql3/Cql.g
@@ -33,6 +33,7 @@ options {
     import java.util.List;
     import java.util.Map;
 
+    import org.apache.cassandra.auth.Permission;
     import org.apache.cassandra.cql3.statements.*;
     import org.apache.cassandra.utils.Pair;
     import org.apache.cassandra.thrift.ConsistencyLevel;
@@ -140,6 +141,9 @@ cqlStatement returns [ParsedStatement stmt]
     | st12=dropColumnFamilyStatement   { $stmt = st12; }
     | st13=dropIndexStatement          { $stmt = st13; }
     | st14=alterTableStatement         { $stmt = st14; }
+    | st15=grantStatement              { $stmt = st15; }
+    | st16=revokeStatement             { $stmt = st16; }
+    | st17=listGrantsStatement         { $stmt = st17; }
     ;
 
 /*
@@ -434,7 +438,51 @@ truncateStatement returns [TruncateStatement stmt]
     : K_TRUNCATE cf=columnFamilyName { $stmt = new TruncateStatement(cf); }
     ;
 
+/**
+ * GRANT <permission> ON <resource> TO <username> [WITH GRANT OPTION]
+ */
+grantStatement returns [GrantStatement stmt]
+    @init { boolean withGrant = false; }
+    : K_GRANT
+          permission
+      K_ON
+          resource=columnFamilyName
+      K_TO
+          user=(IDENT | STRING_LITERAL)
+      (K_WITH K_GRANT K_OPTION { withGrant = true; })?
+      {
+        $stmt = new GrantStatement($permission.perm,
+                                   resource,
+                                   $user.text,
+                                   withGrant);
+      }
+    ;
 
+/**
+ * REVOKE <permission> ON <resource> FROM <username>
+ */
+revokeStatement returns [RevokeStatement stmt]
+    : K_REVOKE
+        permission
+      K_ON
+        resource=columnFamilyName
+      K_FROM
+        user=(IDENT | STRING_LITERAL)
+      {
+        $stmt = new RevokeStatement($permission.perm,
+                                    $user.text,
+                                    resource);
+      }
+    ;
+
+listGrantsStatement returns [ListGrantsStatement stmt]
+    : K_LIST K_GRANTS K_FOR username=(IDENT | STRING_LITERAL) { $stmt = new ListGrantsStatement($username.text); }
+    ;
+
+permission returns [Permission perm]
+    : p=(K_DESCRIBE | K_USE | K_CREATE | K_ALTER | K_DROP | K_SELECT | K_INSERT | K_UPDATE | K_DELETE | K_FULL_ACCESS | K_NO_ACCESS)
+    { $perm = Permission.valueOf($p.text.toUpperCase()); }
+    ;
 /** DEFINITIONS **/
 
 // Column Identifiers
@@ -571,10 +619,11 @@ K_UPDATE:      U P D A T E;
 K_WITH:        W I T H;
 K_LIMIT:       L I M I T;
 K_USING:       U S I N G;
+K_ALL:         A L L;
 K_CONSISTENCY: C O N S I S T E N C Y;
 K_LEVEL:       ( O N E
                | Q U O R U M
-               | A L L
+               | K_ALL
                | A N Y
                | L O C A L '_' Q U O R U M
                | E A C H '_' Q U O R U M
@@ -598,6 +647,7 @@ K_COLUMNFAMILY:( C O L U M N F A M I L Y
                  | T A B L E );
 K_INDEX:       I N D E X;
 K_ON:          O N;
+K_TO:          T O;
 K_DROP:        D R O P;
 K_PRIMARY:     P R I M A R Y;
 K_INTO:        I N T O;
@@ -613,8 +663,18 @@ K_ORDER:       O R D E R;
 K_BY:          B Y;
 K_ASC:         A S C;
 K_DESC:        D E S C;
-K_CLUSTERING:  C L U S T E R I N G;
+K_GRANT:       G R A N T;
+K_GRANTS:      G R A N T S;
+K_REVOKE:      R E V O K E;
+K_OPTION:      O P T I O N;
+K_DESCRIBE:    D E S C R I B E;
+K_FOR:         F O R;
+K_LIST:        L I S T;
+K_FULL_ACCESS: F U L L '_' A C C E S S;
+K_NO_ACCESS:   N O '_' A C C E S S;
 
+
+K_CLUSTERING:  C L U S T E R I N G;
 K_ASCII:       A S C I I;
 K_BIGINT:      B I G I N T;
 K_BLOB:        B L O B;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/cql3/QueryProcessor.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/QueryProcessor.java b/src/java/org/apache/cassandra/cql3/QueryProcessor.java
index 4f08bac..6c155ed 100644
--- a/src/java/org/apache/cassandra/cql3/QueryProcessor.java
+++ b/src/java/org/apache/cassandra/cql3/QueryProcessor.java
@@ -121,6 +121,45 @@ public class QueryProcessor
         return processStatement(getStatement(queryString, clientState).statement, clientState, Collections.<ByteBuffer>emptyList());
     }
 
+    public static CqlResult processInternal(String query, ClientState state)
+    {
+        try
+        {
+            CQLStatement statement = getStatement(query, state).statement;
+
+            statement.validate(state);
+            CqlResult result = statement.execute(state, Collections.<ByteBuffer>emptyList());
+
+            if (result == null || result.rows.isEmpty())
+            {
+                result = new CqlResult();
+                result.type = CqlResultType.VOID;
+            }
+
+            return result;
+        }
+        catch (RecognitionException e)
+        {
+            throw new RuntimeException(e);
+        }
+        catch (UnavailableException e)
+        {
+            throw new RuntimeException(e);
+        }
+        catch (InvalidRequestException e)
+        {
+            throw new AssertionError(e);
+        }
+        catch (TimedOutException e)
+        {
+            throw new RuntimeException(e);
+        }
+        catch (SchemaDisagreementException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
     public static UntypedResultSet resultify(String queryString, Row row)
     {
         SelectStatement ss;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
index 6523a90..7867019 100644
--- a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
@@ -20,11 +20,13 @@ package org.apache.cassandra.cql3.statements;
 
 import java.util.*;
 
+import org.apache.cassandra.auth.Permission;
 import org.apache.cassandra.cql3.*;
 import org.apache.cassandra.config.*;
 import org.apache.cassandra.db.marshal.AbstractType;
 import org.apache.cassandra.db.marshal.CompositeType;
 import org.apache.cassandra.db.marshal.CounterColumnType;
+import org.apache.cassandra.service.ClientState;
 import org.apache.cassandra.service.MigrationManager;
 import org.apache.cassandra.thrift.InvalidRequestException;
 
@@ -51,6 +53,11 @@ public class AlterTableStatement extends SchemaAlteringStatement
         this.cfProps.addAll(propertyMap);
     }
 
+    public void checkAccess(ClientState state) throws InvalidRequestException
+    {
+        state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.ALTER);
+    }
+
     public void announceMigration() throws InvalidRequestException, ConfigurationException
     {
         CFMetaData meta = validateColumnFamily(keyspace(), columnFamily());

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
index 2241b05..155a39d 100644
--- a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
@@ -71,7 +71,7 @@ public class BatchStatement extends ModificationStatement
             // Avoid unnecessary authorizations.
             if (!(cfamsSeen.contains(statement.columnFamily())))
             {
-                state.hasColumnFamilyAccess(statement.keyspace(), statement.columnFamily(), Permission.WRITE);
+                state.hasColumnFamilyAccess(statement.keyspace(), statement.columnFamily(), Permission.UPDATE);
                 cfamsSeen.add(statement.columnFamily());
             }
         }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/cql3/statements/CreateColumnFamilyStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateColumnFamilyStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateColumnFamilyStatement.java
index 8c0806f..763db28 100644
--- a/src/java/org/apache/cassandra/cql3/statements/CreateColumnFamilyStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateColumnFamilyStatement.java
@@ -23,6 +23,8 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+
+import org.apache.cassandra.auth.Permission;
 import org.apache.commons.lang.StringUtils;
 import com.google.common.collect.HashMultiset;
 import com.google.common.collect.Multiset;
@@ -64,6 +66,11 @@ public class CreateColumnFamilyStatement extends SchemaAlteringStatement
         this.properties = properties;
     }
 
+    public void checkAccess(ClientState state) throws InvalidRequestException
+    {
+        state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.CREATE);
+    }
+
     // Column definitions
     private Map<ByteBuffer, ColumnDefinition> getColumns() throws InvalidRequestException
     {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java
index e548638..0250eb2 100644
--- a/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java
@@ -23,10 +23,12 @@ import java.util.Collections;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import org.apache.cassandra.auth.Permission;
 import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.config.ColumnDefinition;
 import org.apache.cassandra.config.ConfigurationException;
 import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.service.ClientState;
 import org.apache.cassandra.service.MigrationManager;
 import org.apache.cassandra.thrift.CfDef;
 import org.apache.cassandra.thrift.ColumnDef;
@@ -49,6 +51,11 @@ public class CreateIndexStatement extends SchemaAlteringStatement
         this.columnName = columnName;
     }
 
+    public void checkAccess(ClientState state) throws InvalidRequestException
+    {
+        state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.ALTER);
+    }
+
     public void announceMigration() throws InvalidRequestException, ConfigurationException
     {
         CFMetaData oldCfm = ThriftValidation.validateColumnFamily(keyspace(), columnFamily());

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java
index 0c347ce..863a869 100644
--- a/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateKeyspaceStatement.java
@@ -21,6 +21,7 @@ package org.apache.cassandra.cql3.statements;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.apache.cassandra.auth.Permission;
 import org.apache.cassandra.config.ConfigurationException;
 import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.config.KSMetaData;
@@ -55,6 +56,11 @@ public class CreateKeyspaceStatement extends SchemaAlteringStatement
         this.attrs = attrs;
     }
 
+    public void checkAccess(ClientState state) throws InvalidRequestException
+    {
+        state.hasKeyspaceAccess(name, Permission.CREATE);
+    }
+
     /**
      * The <code>CqlParser</code> only goes as far as extracting the keyword arguments
      * from these statements, so this method is responsible for processing and

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/cql3/statements/DropColumnFamilyStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/DropColumnFamilyStatement.java b/src/java/org/apache/cassandra/cql3/statements/DropColumnFamilyStatement.java
index e0bd75d..5129431 100644
--- a/src/java/org/apache/cassandra/cql3/statements/DropColumnFamilyStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DropColumnFamilyStatement.java
@@ -20,9 +20,12 @@ package org.apache.cassandra.cql3.statements;
 
 import java.io.IOException;
 
+import org.apache.cassandra.auth.Permission;
 import org.apache.cassandra.config.ConfigurationException;
 import org.apache.cassandra.cql3.CFName;
+import org.apache.cassandra.service.ClientState;
 import org.apache.cassandra.service.MigrationManager;
+import org.apache.cassandra.thrift.InvalidRequestException;
 
 public class DropColumnFamilyStatement extends SchemaAlteringStatement
 {
@@ -31,6 +34,11 @@ public class DropColumnFamilyStatement extends SchemaAlteringStatement
         super(name);
     }
 
+    public void checkAccess(ClientState state) throws InvalidRequestException
+    {
+        state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.DROP);
+    }
+
     public void announceMigration() throws ConfigurationException
     {
         MigrationManager.announceColumnFamilyDrop(keyspace(), columnFamily());

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/cql3/statements/DropIndexStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/DropIndexStatement.java b/src/java/org/apache/cassandra/cql3/statements/DropIndexStatement.java
index 12d04c1..9c3ab5b 100644
--- a/src/java/org/apache/cassandra/cql3/statements/DropIndexStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DropIndexStatement.java
@@ -20,8 +20,10 @@ package org.apache.cassandra.cql3.statements;
 
 import java.io.IOException;
 
+import org.apache.cassandra.auth.Permission;
 import org.apache.cassandra.cql3.*;
 import org.apache.cassandra.config.*;
+import org.apache.cassandra.service.ClientState;
 import org.apache.cassandra.service.MigrationManager;
 import org.apache.cassandra.thrift.CfDef;
 import org.apache.cassandra.thrift.ColumnDef;
@@ -37,6 +39,11 @@ public class DropIndexStatement extends SchemaAlteringStatement
         this.indexName = indexName;
     }
 
+    public void checkAccess(ClientState state) throws InvalidRequestException
+    {
+        state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.ALTER);
+    }
+
     public void announceMigration() throws InvalidRequestException, ConfigurationException
     {
         CFMetaData updatedCfm = null;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/cql3/statements/DropKeyspaceStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/DropKeyspaceStatement.java b/src/java/org/apache/cassandra/cql3/statements/DropKeyspaceStatement.java
index 82dea1b..fb325dc 100644
--- a/src/java/org/apache/cassandra/cql3/statements/DropKeyspaceStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DropKeyspaceStatement.java
@@ -18,8 +18,7 @@
  */
 package org.apache.cassandra.cql3.statements;
 
-import java.io.IOException;
-
+import org.apache.cassandra.auth.Permission;
 import org.apache.cassandra.config.ConfigurationException;
 import org.apache.cassandra.service.ClientState;
 import org.apache.cassandra.service.MigrationManager;
@@ -37,6 +36,11 @@ public class DropKeyspaceStatement extends SchemaAlteringStatement
         this.keyspace = keyspace;
     }
 
+    public void checkAccess(ClientState state) throws InvalidRequestException
+    {
+        state.hasKeyspaceAccess(keyspace, Permission.DROP);
+    }
+
     @Override
     public void validate(ClientState state) throws InvalidRequestException, SchemaDisagreementException
     {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/cql3/statements/GrantStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/GrantStatement.java b/src/java/org/apache/cassandra/cql3/statements/GrantStatement.java
new file mode 100644
index 0000000..556501a
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/GrantStatement.java
@@ -0,0 +1,66 @@
+/**
+ * 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.cassandra.cql3.statements;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.cql3.CFName;
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.thrift.*;
+
+public class GrantStatement extends ParsedStatement implements CQLStatement
+{
+    private final Permission permission;
+    private final CFName resource;
+    private final String username;
+    private final boolean grantOption;
+
+    public GrantStatement(Permission permission, CFName resource, String username, boolean grantOption)
+    {
+        this.permission = permission;
+        this.resource = resource;
+        this.username = username;
+        this.grantOption = grantOption;
+    }
+
+    public int getBoundsTerms()
+    {
+        return 0;
+    }
+
+    public void checkAccess(ClientState state) throws InvalidRequestException
+    {}
+
+    public void validate(ClientState state) throws InvalidRequestException, SchemaDisagreementException
+    {}
+
+    public CqlResult execute(ClientState state, List<ByteBuffer> variables) throws InvalidRequestException, UnavailableException, TimedOutException, SchemaDisagreementException
+    {
+        state.grantPermission(permission, username, resource, grantOption);
+        return null;
+    }
+
+    public Prepared prepare() throws InvalidRequestException
+    {
+        return new Prepared(this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/cql3/statements/ListGrantsStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/ListGrantsStatement.java b/src/java/org/apache/cassandra/cql3/statements/ListGrantsStatement.java
new file mode 100644
index 0000000..c26f9cf
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/ListGrantsStatement.java
@@ -0,0 +1,53 @@
+/**
+ * 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.cassandra.cql3.statements;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.thrift.*;
+
+public class ListGrantsStatement extends ParsedStatement implements CQLStatement
+{
+    private final String username;
+
+    public ListGrantsStatement(String username)
+    {
+        this.username = username;
+    }
+
+    public void checkAccess(ClientState state) throws InvalidRequestException
+    {}
+
+    public void validate(ClientState state) throws InvalidRequestException, SchemaDisagreementException
+    {}
+
+    public CqlResult execute(ClientState state, List<ByteBuffer> variables) throws InvalidRequestException, UnavailableException, TimedOutException, SchemaDisagreementException
+    {
+        return state.listPermissions(username);
+    }
+
+    @Override
+    public Prepared prepare() throws InvalidRequestException
+    {
+        return new Prepared(this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
index 7899e41..e47f7f2 100644
--- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
@@ -62,7 +62,7 @@ public abstract class ModificationStatement extends CFStatement implements CQLSt
 
     public void checkAccess(ClientState state) throws InvalidRequestException
     {
-        state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.WRITE);
+        state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.UPDATE);
     }
 
     public void validate(ClientState state) throws InvalidRequestException

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/cql3/statements/RevokeStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/RevokeStatement.java b/src/java/org/apache/cassandra/cql3/statements/RevokeStatement.java
new file mode 100644
index 0000000..8236a7e
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/RevokeStatement.java
@@ -0,0 +1,66 @@
+/**
+ * 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.cassandra.cql3.statements;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.cql3.CFName;
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.thrift.*;
+
+public class RevokeStatement extends ParsedStatement implements CQLStatement
+{
+    private final Permission permission;
+    private final String from;
+    private final CFName resource;
+
+    public RevokeStatement(Permission permission, String from, CFName resource)
+    {
+        this.permission = permission;
+        this.from = from;
+        this.resource = resource;
+    }
+
+    public int getBoundsTerms()
+    {
+        return 0;
+    }
+
+    public void checkAccess(ClientState state) throws InvalidRequestException
+    {
+    }
+
+    public void validate(ClientState state) throws InvalidRequestException, SchemaDisagreementException
+    {
+    }
+
+    public CqlResult execute(ClientState state, List<ByteBuffer> variables) throws InvalidRequestException, UnavailableException, TimedOutException, SchemaDisagreementException
+    {
+        state.revokePermission(permission, from, resource);
+        return null;
+    }
+
+    public Prepared prepare() throws InvalidRequestException
+    {
+        return new Prepared(this);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java b/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java
index a88ee2b..e49945b 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java
@@ -18,19 +18,11 @@
  */
 package org.apache.cassandra.cql3.statements;
 
-import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
 
 import org.apache.cassandra.cql3.CQLStatement;
-import org.apache.cassandra.auth.Permission;
-import org.apache.cassandra.db.migration.*;
-import org.apache.cassandra.concurrent.Stage;
-import org.apache.cassandra.concurrent.StageManager;
 import org.apache.cassandra.config.ConfigurationException;
 import org.apache.cassandra.config.Schema;
 import org.apache.cassandra.cql3.CFName;
@@ -78,14 +70,6 @@ public abstract class SchemaAlteringStatement extends CFStatement implements CQL
 
     public abstract void announceMigration() throws InvalidRequestException, ConfigurationException;
 
-    public void checkAccess(ClientState state) throws InvalidRequestException
-    {
-        if (isColumnFamilyLevel)
-            state.hasColumnFamilySchemaAccess(keyspace(), Permission.WRITE);
-        else
-            state.hasKeyspaceSchemaAccess(Permission.WRITE);
-    }
-
     @Override
     public void validate(ClientState state) throws InvalidRequestException, SchemaDisagreementException
     {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index 2f47f86..a4d64cf 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -109,7 +109,7 @@ public class SelectStatement implements CQLStatement
 
     public void checkAccess(ClientState state) throws InvalidRequestException
     {
-        state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.READ);
+        state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.SELECT);
     }
 
     public void validate(ClientState state) throws InvalidRequestException

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java b/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java
index ca37dae..9e1661e 100644
--- a/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java
@@ -46,7 +46,7 @@ public class TruncateStatement extends CFStatement implements CQLStatement
 
     public void checkAccess(ClientState state) throws InvalidRequestException
     {
-        state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.WRITE);
+        state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.DELETE);
     }
 
     public void validate(ClientState state) throws InvalidRequestException

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/service/ClientState.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/service/ClientState.java b/src/java/org/apache/cassandra/service/ClientState.java
index 5ef3bad..499486b 100644
--- a/src/java/org/apache/cassandra/service/ClientState.java
+++ b/src/java/org/apache/cassandra/service/ClientState.java
@@ -20,13 +20,13 @@ package org.apache.cassandra.service;
 
 import java.util.*;
 
+import org.apache.cassandra.auth.*;
+import org.apache.cassandra.cql3.CFName;
+import org.apache.cassandra.thrift.CqlResult;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import org.apache.cassandra.auth.AuthenticatedUser;
-import org.apache.cassandra.auth.Permission;
-import org.apache.cassandra.auth.Resources;
 import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.config.Schema;
 import org.apache.cassandra.cql.CQLStatement;
@@ -147,20 +147,7 @@ public class ClientState
         cql3Prepared.clear();
     }
 
-    /**
-     * Confirms that the client thread has the given Permission for the Keyspace list.
-     */
-    public void hasKeyspaceSchemaAccess(Permission perm) throws InvalidRequestException
-    {
-        validateLogin();
-
-        resourceClear();
-        Set<Permission> perms = DatabaseDescriptor.getAuthority().authorize(user, resource);
-
-        hasAccess(user, perms, perm, resource);
-    }
-
-    public void hasColumnFamilySchemaAccess(Permission perm) throws InvalidRequestException
+    public void hasKeyspaceAccess(String keyspace, Permission perm) throws InvalidRequestException
     {
         hasColumnFamilySchemaAccess(keyspace, perm);
     }
@@ -175,7 +162,7 @@ public class ClientState
         validateKeyspace(keyspace);
 
         // hardcode disallowing messing with system keyspace
-        if (keyspace.equalsIgnoreCase(Table.SYSTEM_TABLE) && perm == Permission.WRITE)
+        if (keyspace.equalsIgnoreCase(Table.SYSTEM_TABLE) && (perm != Permission.USE))
             throw new InvalidRequestException("system keyspace is not user-modifiable");
 
         resourceClear();
@@ -201,6 +188,12 @@ public class ClientState
 
         resourceClear();
         resource.add(keyspace);
+
+        // check if keyspace access is set to Permission.ALL
+        // (which means that user has all access on keyspace and it's underlying elements)
+        if (DatabaseDescriptor.getAuthority().authorize(user, resource).contains(Permission.ALL))
+            return;
+
         resource.add(columnFamily);
         Set<Permission> perms = DatabaseDescriptor.getAuthority().authorize(user, resource);
 
@@ -216,17 +209,54 @@ public class ClientState
     private static void validateKeyspace(String keyspace) throws InvalidRequestException
     {
         if (keyspace == null)
+        {
             throw new InvalidRequestException("You have not set a keyspace for this session");
+        }
     }
 
-    private static void hasAccess(AuthenticatedUser user, Set<Permission> perms, Permission perm, List<Object> resource) throws InvalidRequestException
+    private static void hasAccess(AuthenticatedUser user, Set<Permission> perms, Permission perm, List<Object> resource) throws PermissionDenied
     {
-        if (perms.contains(perm))
-            return;
-        throw new InvalidRequestException(String.format("%s does not have permission %s for %s",
-                                                        user,
-                                                        perm,
-                                                        Resources.toString(resource)));
+        if (perms.contains(Permission.FULL_ACCESS))
+            return; // full access
+
+        if (perms.contains(Permission.NO_ACCESS))
+            throw new PermissionDenied(String.format("%s does not have permission %s for %s",
+                                                     user,
+                                                     perm,
+                                                     Resources.toString(resource)));
+
+        boolean granular = false;
+
+        for (Permission p : perms)
+        {
+            // mixing of old and granular permissions is denied by IAuthorityContainer
+            // and CQL grammar so it's name to assume that once a granular permission is found
+            // all other permissions are going to be a subset of Permission.GRANULAR_PERMISSIONS
+            if (Permission.GRANULAR_PERMISSIONS.contains(p))
+            {
+                granular = true;
+                break;
+            }
+        }
+
+        if (granular)
+        {
+            if (perms.contains(perm))
+                return; // user has a given permission, perm is always one of Permission.GRANULAR_PERMISSIONS
+        }
+        else
+        {
+            for (Permission p : perms)
+            {
+                if (Permission.oldToNew.get(p).contains(perm))
+                    return;
+            }
+        }
+
+        throw new PermissionDenied(String.format("%s does not have permission %s for %s",
+                                                  user,
+                                                  perm,
+                                                  Resources.toString(resource)));
     }
 
     /**
@@ -278,4 +308,19 @@ public class ClientState
 
         return new SemanticVersion[]{ cql, cql3 };
     }
+
+    public void grantPermission(Permission permission, String to, CFName on, boolean grantOption) throws InvalidRequestException
+    {
+        DatabaseDescriptor.getAuthorityContainer().grant(user, permission, to, on, grantOption);
+    }
+
+    public void revokePermission(Permission permission, String from, CFName resource) throws InvalidRequestException
+    {
+        DatabaseDescriptor.getAuthorityContainer().revoke(user, permission, from, resource);
+    }
+
+    public CqlResult listPermissions(String username) throws InvalidRequestException
+    {
+        return DatabaseDescriptor.getAuthorityContainer().listPermissions(username);
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/aba5a376/src/java/org/apache/cassandra/thrift/CassandraServer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/thrift/CassandraServer.java b/src/java/org/apache/cassandra/thrift/CassandraServer.java
index 58c2be8..ea2465e 100644
--- a/src/java/org/apache/cassandra/thrift/CassandraServer.java
+++ b/src/java/org/apache/cassandra/thrift/CassandraServer.java
@@ -35,6 +35,7 @@ import org.slf4j.LoggerFactory;
 
 import org.antlr.runtime.RecognitionException;
 import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.auth.PermissionDenied;
 import org.apache.cassandra.config.*;
 import org.apache.cassandra.cql.CQLStatement;
 import org.apache.cassandra.cql.QueryProcessor;
@@ -305,7 +306,7 @@ public class CassandraServer implements Cassandra.Iface
         logger.debug("get_slice");
 
         ClientState cState = state();
-        cState.hasColumnFamilyAccess(column_parent.column_family, Permission.READ);
+        cState.hasColumnFamilyAccess(column_parent.column_family, Permission.SELECT);
         return multigetSliceInternal(cState.getKeyspace(), Collections.singletonList(key), column_parent, predicate, consistency_level).get(key);
     }
 
@@ -315,7 +316,7 @@ public class CassandraServer implements Cassandra.Iface
         logger.debug("multiget_slice");
 
         ClientState cState = state();
-        cState.hasColumnFamilyAccess(column_parent.column_family, Permission.READ);
+        cState.hasColumnFamilyAccess(column_parent.column_family, Permission.SELECT);
         return multigetSliceInternal(cState.getKeyspace(), keys, column_parent, predicate, consistency_level);
     }
 
@@ -353,7 +354,7 @@ public class CassandraServer implements Cassandra.Iface
     throws InvalidRequestException, NotFoundException, UnavailableException, TimedOutException
     {
         ClientState cState = state();
-        cState.hasColumnFamilyAccess(column_path.column_family, Permission.READ);
+        cState.hasColumnFamilyAccess(column_path.column_family, Permission.SELECT);
         String keyspace = cState.getKeyspace();
 
         CFMetaData metadata = ThriftValidation.validateColumnFamily(keyspace, column_path.column_family);
@@ -392,7 +393,7 @@ public class CassandraServer implements Cassandra.Iface
         logger.debug("get_count");
 
         ClientState cState = state();
-        cState.hasColumnFamilyAccess(column_parent.column_family, Permission.READ);
+        cState.hasColumnFamilyAccess(column_parent.column_family, Permission.SELECT);
         Table table = Table.open(cState.getKeyspace());
         ColumnFamilyStore cfs = table.getColumnFamilyStore(column_parent.column_family);
 
@@ -465,7 +466,7 @@ public class CassandraServer implements Cassandra.Iface
         logger.debug("multiget_count");
 
         ClientState cState = state();
-        cState.hasColumnFamilyAccess(column_parent.column_family, Permission.READ);
+        cState.hasColumnFamilyAccess(column_parent.column_family, Permission.SELECT);
         String keyspace = cState.getKeyspace();
 
         Map<ByteBuffer, Integer> counts = new HashMap<ByteBuffer, Integer>();
@@ -481,7 +482,7 @@ public class CassandraServer implements Cassandra.Iface
     throws InvalidRequestException, UnavailableException, TimedOutException
     {
         ClientState cState = state();
-        cState.hasColumnFamilyAccess(column_parent.column_family, Permission.WRITE);
+        cState.hasColumnFamilyAccess(column_parent.column_family, Permission.UPDATE);
 
         CFMetaData metadata = ThriftValidation.validateColumnFamily(cState.getKeyspace(), column_parent.column_family, false);
         ThriftValidation.validateKey(metadata, key);
@@ -539,7 +540,7 @@ public class CassandraServer implements Cassandra.Iface
                 // Avoid unneeded authorizations
                 if (!(cfamsSeen.contains(cfName)))
                 {
-                    cState.hasColumnFamilyAccess(cfName, Permission.WRITE);
+                    cState.hasColumnFamilyAccess(cfName, Permission.UPDATE);
                     cfamsSeen.add(cfName);
                 }
 
@@ -594,7 +595,7 @@ public class CassandraServer implements Cassandra.Iface
     throws InvalidRequestException, UnavailableException, TimedOutException
     {
         ClientState cState = state();
-        cState.hasColumnFamilyAccess(column_path.column_family, Permission.WRITE);
+        cState.hasColumnFamilyAccess(column_path.column_family, Permission.DELETE);
 
         CFMetaData metadata = ThriftValidation.validateColumnFamily(cState.getKeyspace(), column_path.column_family, isCommutativeOp);
         ThriftValidation.validateKey(metadata, key);
@@ -645,7 +646,7 @@ public class CassandraServer implements Cassandra.Iface
 
     public KsDef describe_keyspace(String table) throws NotFoundException, InvalidRequestException
     {
-        state().hasKeyspaceSchemaAccess(Permission.READ);
+        state().hasKeyspaceAccess(table, Permission.USE);
 
         KSMetaData ksm = Schema.instance.getTableDefinition(table);
         if (ksm == null)
@@ -661,7 +662,7 @@ public class CassandraServer implements Cassandra.Iface
 
         ClientState cState = state();
         String keyspace = cState.getKeyspace();
-        cState.hasColumnFamilyAccess(column_parent.column_family, Permission.READ);
+        cState.hasColumnFamilyAccess(column_parent.column_family, Permission.SELECT);
 
         CFMetaData metadata = ThriftValidation.validateColumnFamily(keyspace, column_parent.column_family);
         ThriftValidation.validateColumnParent(metadata, column_parent);
@@ -716,7 +717,7 @@ public class CassandraServer implements Cassandra.Iface
 
         ClientState cState = state();
         String keyspace = cState.getKeyspace();
-        cState.hasColumnFamilyAccess(column_family, Permission.READ);
+        cState.hasColumnFamilyAccess(column_family, Permission.SELECT);
 
         CFMetaData metadata = ThriftValidation.validateColumnFamily(keyspace, column_family);
         ThriftValidation.validateKeyRange(metadata, null, range);
@@ -786,7 +787,7 @@ public class CassandraServer implements Cassandra.Iface
         logger.debug("scan");
 
         ClientState cState = state();
-        cState.hasColumnFamilyAccess(column_parent.column_family, Permission.READ);
+        cState.hasColumnFamilyAccess(column_parent.column_family, Permission.SELECT);
         String keyspace = cState.getKeyspace();
         CFMetaData metadata = ThriftValidation.validateColumnFamily(keyspace, column_parent.column_family, false);
         ThriftValidation.validateColumnParent(metadata, column_parent);
@@ -825,16 +826,20 @@ public class CassandraServer implements Cassandra.Iface
 
     public List<KsDef> describe_keyspaces() throws TException, InvalidRequestException
     {
-        state().hasKeyspaceSchemaAccess(Permission.READ);
-
         Set<String> keyspaces = Schema.instance.getTables();
         List<KsDef> ksset = new ArrayList<KsDef>(keyspaces.size());
+
         for (String ks : keyspaces)
         {
             try
             {
                 ksset.add(describe_keyspace(ks));
             }
+            catch (PermissionDenied e)
+            {
+                if (logger.isDebugEnabled())
+                    logger.debug("PermissionDenied: " + e.getMessage());
+            }
             catch (NotFoundException nfe)
             {
                 logger.info("Failed to find metadata for keyspace '" + ks + "'. Continuing... ");
@@ -914,7 +919,7 @@ public class CassandraServer implements Cassandra.Iface
     throws InvalidRequestException, SchemaDisagreementException, TException
     {
         logger.debug("add_column_family");
-        state().hasColumnFamilySchemaAccess(Permission.WRITE);
+        state().hasColumnFamilyAccess(cf_def.name, Permission.CREATE);
 
         validateSchemaAgreement();
 
@@ -940,7 +945,7 @@ public class CassandraServer implements Cassandra.Iface
         logger.debug("drop_column_family");
 
         ClientState cState = state();
-        cState.hasColumnFamilySchemaAccess(Permission.WRITE);
+        cState.hasColumnFamilyAccess(column_family, Permission.DROP);
         validateSchemaAgreement();
 
         try
@@ -961,7 +966,7 @@ public class CassandraServer implements Cassandra.Iface
     {
         logger.debug("add_keyspace");
         ThriftValidation.validateKeyspaceNotSystem(ks_def.name);
-        state().hasKeyspaceSchemaAccess(Permission.WRITE);
+        state().hasKeyspaceAccess(ks_def.name, Permission.CREATE);
         validateSchemaAgreement();
         ThriftValidation.validateKeyspaceNotYetExisting(ks_def.name);
 
@@ -1000,7 +1005,7 @@ public class CassandraServer implements Cassandra.Iface
     {
         logger.debug("drop_keyspace");
         ThriftValidation.validateKeyspaceNotSystem(keyspace);
-        state().hasKeyspaceSchemaAccess(Permission.WRITE);
+        state().hasKeyspaceAccess(keyspace, Permission.DROP);
         validateSchemaAgreement();
 
         try
@@ -1024,7 +1029,7 @@ public class CassandraServer implements Cassandra.Iface
     {
         logger.debug("update_keyspace");
         ThriftValidation.validateKeyspaceNotSystem(ks_def.name);
-        state().hasKeyspaceSchemaAccess(Permission.WRITE);
+        state().hasKeyspaceAccess(ks_def.name, Permission.ALTER);
         ThriftValidation.validateTable(ks_def.name);
         if (ks_def.getCf_defs() != null && ks_def.getCf_defs().size() > 0)
             throw new InvalidRequestException("Keyspace update must not contain any column family definitions.");
@@ -1047,7 +1052,7 @@ public class CassandraServer implements Cassandra.Iface
     throws InvalidRequestException, SchemaDisagreementException, TException
     {
         logger.debug("update_column_family");
-        state().hasColumnFamilySchemaAccess(Permission.WRITE);
+        state().hasColumnFamilyAccess(cf_def.name, Permission.ALTER);
         if (cf_def.keyspace == null || cf_def.name == null)
             throw new InvalidRequestException("Keyspace and CF name must be set.");
         CFMetaData oldCfm = Schema.instance.getCFMetaData(cf_def.keyspace, cf_def.name);
@@ -1084,7 +1089,7 @@ public class CassandraServer implements Cassandra.Iface
     {
         ClientState cState = state();
         logger.debug("truncating {} in {}", cfname, cState.getKeyspace());
-        cState.hasColumnFamilyAccess(cfname, Permission.WRITE);
+        cState.hasColumnFamilyAccess(cfname, Permission.DELETE);
         try
         {
             schedule(DatabaseDescriptor.getRpcTimeout());
@@ -1129,7 +1134,7 @@ public class CassandraServer implements Cassandra.Iface
         logger.debug("add");
 
         ClientState cState = state();
-        cState.hasColumnFamilyAccess(column_parent.column_family, Permission.WRITE);
+        cState.hasColumnFamilyAccess(column_parent.column_family, Permission.UPDATE);
         String keyspace = cState.getKeyspace();
 
         CFMetaData metadata = ThriftValidation.validateColumnFamily(keyspace, column_parent.column_family, true);