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 23:45:18 UTC
[2/8] 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
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/bcec7a5e
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/bcec7a5e
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/bcec7a5e
Branch: refs/heads/trunk
Commit: bcec7a5e225ccc5be16c224b397abab267fe2a46
Parents: 0388a33
Author: Pavel Yaskevich <xe...@apache.org>
Authored: Mon Sep 10 18:27:26 2012 +0300
Committer: Pavel Yaskevich <xe...@apache.org>
Committed: Tue Sep 11 00:37:10 2012 +0300
----------------------------------------------------------------------
CHANGES.txt | 2 +
src/java/org/apache/cassandra/auth/IAuthority.java | 1 +
.../org/apache/cassandra/auth/IAuthority2.java | 70 +++++++++++
.../apache/cassandra/auth/IAuthorityContainer.java | 84 ++++++++++++++
src/java/org/apache/cassandra/auth/Permission.java | 31 +++++-
.../apache/cassandra/auth/PermissionDenied.java | 34 ++++++
.../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 | 20 ++--
.../org/apache/cassandra/cql/UpdateStatement.java | 2 +-
src/java/org/apache/cassandra/cql3/CFName.java | 5 +
src/java/org/apache/cassandra/cql3/Cql.g | 63 ++++++++++-
.../cql3/statements/AlterTableStatement.java | 8 ++
.../cassandra/cql3/statements/BatchStatement.java | 2 +-
.../statements/CreateColumnFamilyStatement.java | 7 +
.../cql3/statements/CreateIndexStatement.java | 8 ++
.../cql3/statements/CreateKeyspaceStatement.java | 6 +
.../cql3/statements/DropColumnFamilyStatement.java | 11 ++-
.../cql3/statements/DropIndexStatement.java | 12 ++-
.../cql3/statements/DropKeyspaceStatement.java | 8 +-
.../cassandra/cql3/statements/GrantStatement.java | 68 +++++++++++
.../cql3/statements/ListGrantsStatement.java | 55 +++++++++
.../cql3/statements/ModificationStatement.java | 2 +-
.../cassandra/cql3/statements/RevokeStatement.java | 68 +++++++++++
.../cql3/statements/SchemaAlteringStatement.java | 9 +--
.../cassandra/cql3/statements/SelectStatement.java | 2 +-
.../cql3/statements/TruncateStatement.java | 2 +-
.../org/apache/cassandra/service/ClientState.java | 90 +++++++++++----
.../apache/cassandra/thrift/CassandraServer.java | 56 ++++++----
30 files changed, 671 insertions(+), 80 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bcec7a5e/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index a6141f9..26c361a 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -64,6 +64,8 @@
* (cql3) improves ORDER BY validation (CASSANDRA-4624)
* Fix potential deadlock during counter writes (CASSANDRA-4578)
* Fix cql error with ORDER BY when using IN (CASSANDRA-4612)
+ * Improve IAuthority interface by introducing fine-grained
+ access permissions and grant/revoke commands (CASSANDRA-4490)
1.1.5
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bcec7a5e/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 5bbdbe1..8b37470 100644
--- a/src/java/org/apache/cassandra/auth/IAuthority.java
+++ b/src/java/org/apache/cassandra/auth/IAuthority.java
@@ -23,6 +23,7 @@ import java.util.List;
import org.apache.cassandra.exceptions.ConfigurationException;
/**
+ *
* Cassandra's resource hierarchy looks something like:
* {{/cassandra/keyspaces/$ks_name/...}}
*
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bcec7a5e/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..2933f32
--- /dev/null
+++ b/src/java/org/apache/cassandra/auth/IAuthority2.java
@@ -0,0 +1,70 @@
+/**
+ * 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.exceptions.InvalidRequestException;
+import org.apache.cassandra.exceptions.UnauthorizedException;
+import org.apache.cassandra.transport.messages.ResultMessage;
+
+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 UnauthorizedException, 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 UnauthorizedException, 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 ResultMessage listPermissions(String username) throws UnauthorizedException, InvalidRequestException;
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bcec7a5e/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..433908f
--- /dev/null
+++ b/src/java/org/apache/cassandra/auth/IAuthorityContainer.java
@@ -0,0 +1,84 @@
+/**
+ * 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.exceptions.InvalidRequestException;
+import org.apache.cassandra.exceptions.UnauthorizedException;
+import org.apache.cassandra.thrift.CqlResult;
+import org.apache.cassandra.transport.messages.ResultMessage;
+
+/**
+ * 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 UnauthorizedException, 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 UnauthorizedException, InvalidRequestException
+ {
+ if (dynamicAuthority == null)
+ throw new InvalidRequestException("REVOKE operation is not supported by your authority: " + authority);
+
+ dynamicAuthority.revoke(revoker, permission, from, resource);
+ }
+
+ public ResultMessage listPermissions(String username) throws UnauthorizedException, 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/bcec7a5e/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 ae73e22..11f5e03 100644
--- a/src/java/org/apache/cassandra/auth/Permission.java
+++ b/src/java/org/apache/cassandra/auth/Permission.java
@@ -18,6 +18,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.
@@ -26,9 +28,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/bcec7a5e/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/bcec7a5e/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 467a6c6..97106f1 100644
--- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
+++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
@@ -30,10 +30,7 @@ import com.google.common.primitives.Longs;
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;
@@ -80,6 +77,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";
@@ -213,6 +211,8 @@ public class DatabaseDescriptor
authenticator.validateConfiguration();
authority.validateConfiguration();
+ authorityContainer = new IAuthorityContainer(authority);
+
/* Hashing strategy */
if (conf.partitioner == null)
{
@@ -469,6 +469,9 @@ public class DatabaseDescriptor
Schema.instance.setTableDefinition(ksmd);
}
+ // setup schema required for authorization
+ authorityContainer.setup();
+
/* Load the seeds for node contact points */
if (conf.seed_provider == null)
{
@@ -591,6 +594,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/bcec7a5e/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 88f3037..50cbb91 100644
--- a/src/java/org/apache/cassandra/config/KSMetaData.java
+++ b/src/java/org/apache/cassandra/config/KSMetaData.java
@@ -63,7 +63,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/bcec7a5e/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 e7d73e8..45d6e74 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/bcec7a5e/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 fcf4d5c..955d07b 100644
--- a/src/java/org/apache/cassandra/cql/QueryProcessor.java
+++ b/src/java/org/apache/cassandra/cql/QueryProcessor.java
@@ -238,7 +238,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());
}
@@ -427,7 +427,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()
@@ -558,6 +558,7 @@ public class QueryProcessor
case UPDATE:
UpdateStatement update = (UpdateStatement)statement.statement;
update.getConsistencyLevel().validateForWrite(keyspace);
+ clientState.hasColumnFamilyAccess(keyspace, update.getColumnFamily(), Permission.UPDATE);
batchUpdate(clientState, Collections.singletonList(update), update.getConsistencyLevel(), variables);
result.type = CqlResultType.VOID;
return result;
@@ -602,7 +603,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
{
@@ -624,6 +625,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)
{
@@ -639,7 +641,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);
try
{
@@ -661,7 +663,7 @@ public class QueryProcessor
case CREATE_COLUMNFAMILY:
CreateColumnFamilyStatement createCf = (CreateColumnFamilyStatement)statement.statement;
- clientState.hasColumnFamilySchemaAccess(Permission.WRITE);
+ clientState.hasColumnFamilySchemaAccess(createCf.getName(), Permission.CREATE);
try
{
@@ -679,7 +681,7 @@ public class QueryProcessor
case CREATE_INDEX:
CreateIndexStatement createIdx = (CreateIndexStatement)statement.statement;
- clientState.hasColumnFamilySchemaAccess(Permission.WRITE);
+ clientState.hasColumnFamilyAccess(keyspace, createIdx.getColumnFamily(), Permission.ALTER);
CFMetaData oldCfm = Schema.instance.getCFMetaData(keyspace, createIdx.getColumnFamily());
if (oldCfm == null)
throw new InvalidRequestException("No such column family: " + createIdx.getColumnFamily());
@@ -747,7 +749,7 @@ public class QueryProcessor
case DROP_KEYSPACE:
String deleteKeyspace = (String)statement.statement;
ThriftValidation.validateKeyspaceNotSystem(deleteKeyspace);
- clientState.hasKeyspaceSchemaAccess(Permission.WRITE);
+ clientState.hasKeyspaceAccess(deleteKeyspace, Permission.DROP);
try
{
@@ -765,7 +767,7 @@ public class QueryProcessor
case DROP_COLUMNFAMILY:
String deleteColumnFamily = (String)statement.statement;
- clientState.hasColumnFamilySchemaAccess(Permission.WRITE);
+ clientState.hasColumnFamilyAccess(keyspace, deleteColumnFamily, Permission.DROP);
try
{
@@ -785,7 +787,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);
try
{
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bcec7a5e/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 750cc09..3a47712 100644
--- a/src/java/org/apache/cassandra/cql/UpdateStatement.java
+++ b/src/java/org/apache/cassandra/cql/UpdateStatement.java
@@ -154,7 +154,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/bcec7a5e/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 8446a35..6559642 100644
--- a/src/java/org/apache/cassandra/cql3/CFName.java
+++ b/src/java/org/apache/cassandra/cql3/CFName.java
@@ -49,6 +49,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/bcec7a5e/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 7c02da1..acb6cb1 100644
--- a/src/java/org/apache/cassandra/cql3/Cql.g
+++ b/src/java/org/apache/cassandra/cql3/Cql.g
@@ -35,6 +35,7 @@ options {
import java.util.Map;
import org.apache.cassandra.cql3.operations.*;
+ import org.apache.cassandra.auth.Permission;
import org.apache.cassandra.cql3.statements.*;
import org.apache.cassandra.db.marshal.CollectionType;
import org.apache.cassandra.exceptions.ConfigurationException;
@@ -165,6 +166,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; }
;
/*
@@ -472,7 +476,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
@@ -694,10 +742,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
@@ -721,6 +770,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;
@@ -736,8 +786,17 @@ 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_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/bcec7a5e/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 4e35adb..360d188 100644
--- a/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/AlterTableStatement.java
@@ -20,6 +20,7 @@ package org.apache.cassandra.cql3.statements;
import java.nio.ByteBuffer;
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;
@@ -28,6 +29,8 @@ import org.apache.cassandra.db.marshal.ColumnToCollectionType;
import org.apache.cassandra.db.marshal.CompositeType;
import org.apache.cassandra.db.marshal.CounterColumnType;
import org.apache.cassandra.exceptions.*;
+import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.MigrationManager;
import static org.apache.cassandra.thrift.ThriftValidation.validateColumnFamily;
@@ -53,6 +56,11 @@ public class AlterTableStatement extends SchemaAlteringStatement
this.cfProps = cfProps;
}
+ public void checkAccess(ClientState state) throws UnauthorizedException, 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/bcec7a5e/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 c5ee187..3a286fc 100644
--- a/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/BatchStatement.java
@@ -70,7 +70,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/bcec7a5e/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 b00cea5..e815c40 100644
--- a/src/java/org/apache/cassandra/cql3/statements/CreateColumnFamilyStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateColumnFamilyStatement.java
@@ -22,6 +22,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;
@@ -65,6 +67,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/bcec7a5e/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 8f933ee..fedbf27 100644
--- a/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateIndexStatement.java
@@ -24,12 +24,15 @@ import java.util.Map;
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.exceptions.ConfigurationException;
import org.apache.cassandra.cql3.*;
import org.apache.cassandra.db.index.composites.CompositesIndex;
import org.apache.cassandra.db.marshal.CompositeType;
+import org.apache.cassandra.exceptions.UnauthorizedException;
+import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.MigrationManager;
import org.apache.cassandra.thrift.IndexType;
import org.apache.cassandra.exceptions.InvalidRequestException;
@@ -50,6 +53,11 @@ public class CreateIndexStatement extends SchemaAlteringStatement
this.columnName = columnName;
}
+ public void checkAccess(ClientState state) throws UnauthorizedException, 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/bcec7a5e/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 34d52bd..1d60240 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 @@ import java.util.HashMap;
import java.util.Map;
import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.auth.Permission;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.config.KSMetaData;
import org.apache.cassandra.config.Schema;
@@ -57,6 +58,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/bcec7a5e/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 fe84ad4..cba5944 100644
--- a/src/java/org/apache/cassandra/cql3/statements/DropColumnFamilyStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DropColumnFamilyStatement.java
@@ -17,8 +17,12 @@
*/
package org.apache.cassandra.cql3.statements;
-import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.auth.Permission;
import org.apache.cassandra.cql3.CFName;
+import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.exceptions.UnauthorizedException;
+import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.MigrationManager;
public class DropColumnFamilyStatement extends SchemaAlteringStatement
@@ -28,6 +32,11 @@ public class DropColumnFamilyStatement extends SchemaAlteringStatement
super(name);
}
+ public void checkAccess(ClientState state) throws UnauthorizedException, 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/bcec7a5e/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 5fb2883..3ed391f 100644
--- a/src/java/org/apache/cassandra/cql3/statements/DropIndexStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DropIndexStatement.java
@@ -18,9 +18,14 @@
package org.apache.cassandra.cql3.statements;
+import org.apache.cassandra.auth.Permission;
+import org.apache.cassandra.config.CFMetaData;
+import org.apache.cassandra.config.ColumnDefinition;
+import org.apache.cassandra.config.KSMetaData;
+import org.apache.cassandra.config.Schema;
import org.apache.cassandra.cql3.*;
-import org.apache.cassandra.config.*;
import org.apache.cassandra.exceptions.*;
+import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.MigrationManager;
public class DropIndexStatement extends SchemaAlteringStatement
@@ -33,6 +38,11 @@ public class DropIndexStatement extends SchemaAlteringStatement
this.indexName = indexName;
}
+ public void checkAccess(ClientState state) throws UnauthorizedException, 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/bcec7a5e/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 788ffc1..8e2cfbe 100644
--- a/src/java/org/apache/cassandra/cql3/statements/DropKeyspaceStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/DropKeyspaceStatement.java
@@ -17,9 +17,10 @@
*/
package org.apache.cassandra.cql3.statements;
-
import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.exceptions.RequestValidationException;
+import org.apache.cassandra.auth.Permission;
import org.apache.cassandra.service.ClientState;
import org.apache.cassandra.service.MigrationManager;
import org.apache.cassandra.thrift.ThriftValidation;
@@ -34,6 +35,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 RequestValidationException
{
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bcec7a5e/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..365b9cd
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/GrantStatement.java
@@ -0,0 +1,68 @@
+/**
+ * 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.exceptions.InvalidRequestException;
+import org.apache.cassandra.exceptions.UnauthorizedException;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.transport.messages.ResultMessage;
+
+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 UnauthorizedException, InvalidRequestException
+ {}
+
+ public void validate(ClientState state) throws InvalidRequestException
+ {}
+
+ public ResultMessage execute(ClientState state, List<ByteBuffer> variables) throws UnauthorizedException, InvalidRequestException
+ {
+ 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/bcec7a5e/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..e929d36
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/ListGrantsStatement.java
@@ -0,0 +1,55 @@
+/**
+ * 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.exceptions.InvalidRequestException;
+import org.apache.cassandra.exceptions.UnauthorizedException;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.transport.messages.ResultMessage;
+
+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
+ {}
+
+ public ResultMessage execute(ClientState state, List<ByteBuffer> variables) throws UnauthorizedException, InvalidRequestException
+ {
+ return state.listPermissions(username);
+ }
+
+ @Override
+ public Prepared prepare() throws InvalidRequestException
+ {
+ return new Prepared(this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bcec7a5e/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 8ed22e1..00674e9 100644
--- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
@@ -61,7 +61,7 @@ public abstract class ModificationStatement extends CFStatement implements CQLSt
public void checkAccess(ClientState state) throws InvalidRequestException, UnauthorizedException
{
- 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/bcec7a5e/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..f741642
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/statements/RevokeStatement.java
@@ -0,0 +1,68 @@
+/**
+ * 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.exceptions.InvalidRequestException;
+import org.apache.cassandra.exceptions.UnauthorizedException;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.transport.messages.ResultMessage;
+
+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
+ {
+ }
+
+ public ResultMessage execute(ClientState state, List<ByteBuffer> variables) throws UnauthorizedException, InvalidRequestException
+ {
+ 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/bcec7a5e/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 f00086b..34c957e 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SchemaAlteringStatement.java
@@ -22,6 +22,7 @@ import java.util.List;
import java.util.Map;
import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.auth.Permission;
import org.apache.cassandra.config.Schema;
@@ -71,14 +72,6 @@ public abstract class SchemaAlteringStatement extends CFStatement implements CQL
public abstract void announceMigration() throws RequestValidationException;
- public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException
- {
- if (isColumnFamilyLevel)
- state.hasColumnFamilySchemaAccess(keyspace(), Permission.WRITE);
- else
- state.hasKeyspaceSchemaAccess(Permission.WRITE);
- }
-
@Override
public void validate(ClientState state) throws RequestValidationException
{}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bcec7a5e/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 2a097f5..07c8453 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -102,7 +102,7 @@ public class SelectStatement implements CQLStatement
public void checkAccess(ClientState state) throws InvalidRequestException, UnauthorizedException
{
- 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/bcec7a5e/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 fceaebc..f2a1424 100644
--- a/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/TruncateStatement.java
@@ -44,7 +44,7 @@ public class TruncateStatement extends CFStatement implements CQLStatement
public void checkAccess(ClientState state) throws InvalidRequestException, UnauthorizedException
{
- 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/bcec7a5e/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 6e47950..e96da85 100644
--- a/src/java/org/apache/cassandra/service/ClientState.java
+++ b/src/java/org/apache/cassandra/service/ClientState.java
@@ -19,13 +19,14 @@ 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.cassandra.transport.messages.ResultMessage;
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;
@@ -183,20 +184,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 UnauthorizedException, InvalidRequestException
- {
- validateLogin();
-
- resourceClear();
- Set<Permission> perms = DatabaseDescriptor.getAuthority().authorize(user, resource);
-
- hasAccess(user, perms, perm, resource);
- }
-
- public void hasColumnFamilySchemaAccess(Permission perm) throws UnauthorizedException, InvalidRequestException
+ public void hasKeyspaceAccess(String keyspace, Permission perm) throws UnauthorizedException, InvalidRequestException
{
hasColumnFamilySchemaAccess(keyspace, perm);
}
@@ -211,8 +199,8 @@ public class ClientState
validateKeyspace(keyspace);
// hardcode disallowing messing with system keyspace
- if (keyspace.equalsIgnoreCase(Table.SYSTEM_KS) && perm == Permission.WRITE)
- throw new UnauthorizedException("system keyspace is not user-modifiable");
+ if (keyspace.equalsIgnoreCase(Table.SYSTEM_KS) && (perm != Permission.USE))
+ throw new InvalidRequestException("system keyspace is not user-modifiable");
resourceClear();
resource.add(keyspace);
@@ -237,6 +225,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.FULL_ACCESS))
+ return;
+
resource.add(columnFamily);
Set<Permission> perms = DatabaseDescriptor.getAuthority().authorize(user, resource);
@@ -257,17 +251,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 UnauthorizedException
{
- if (perms.contains(perm))
- return;
+ if (perms.contains(Permission.FULL_ACCESS))
+ return; // full access
+
+ if (perms.contains(Permission.NO_ACCESS))
+ throw new UnauthorizedException(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 UnauthorizedException(String.format("%s does not have permission %s for %s",
user,
perm,
- Resources.toString(resource)));
+ Resources.toString(resource)));
}
/**
@@ -319,4 +350,19 @@ public class ClientState
return new SemanticVersion[]{ cql, cql3 };
}
+
+ public void grantPermission(Permission permission, String to, CFName on, boolean grantOption) throws UnauthorizedException, InvalidRequestException
+ {
+ DatabaseDescriptor.getAuthorityContainer().grant(user, permission, to, on, grantOption);
+ }
+
+ public void revokePermission(Permission permission, String from, CFName resource) throws UnauthorizedException, InvalidRequestException
+ {
+ DatabaseDescriptor.getAuthorityContainer().revoke(user, permission, from, resource);
+ }
+
+ public ResultMessage listPermissions(String username) throws UnauthorizedException, InvalidRequestException
+ {
+ return DatabaseDescriptor.getAuthorityContainer().listPermissions(username);
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/bcec7a5e/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 40f6681..b7815a6 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.Logger;
import org.slf4j.LoggerFactory;
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;
@@ -48,6 +49,7 @@ import org.apache.cassandra.dht.*;
import org.apache.cassandra.exceptions.RequestExecutionException;
import org.apache.cassandra.exceptions.RequestValidationException;
import org.apache.cassandra.exceptions.ReadTimeoutException;
+import org.apache.cassandra.exceptions.UnauthorizedException;
import org.apache.cassandra.io.util.DataOutputBuffer;
import org.apache.cassandra.locator.DynamicEndpointSnitch;
import org.apache.cassandra.scheduler.IRequestScheduler;
@@ -324,7 +326,7 @@ public class CassandraServer implements Cassandra.Iface
try
{
- state().hasColumnFamilyAccess(column_parent.column_family, Permission.READ);
+ state().hasColumnFamilyAccess(column_parent.column_family, Permission.SELECT);
return multigetSliceInternal(state().getKeyspace(), Collections.singletonList(key), column_parent,
predicate, consistency_level).get(key);
}
@@ -361,7 +363,7 @@ public class CassandraServer implements Cassandra.Iface
try
{
- state().hasColumnFamilyAccess(column_parent.column_family, Permission.READ);
+ state().hasColumnFamilyAccess(column_parent.column_family, Permission.SELECT);
return multigetSliceInternal(state().getKeyspace(), keys, column_parent, predicate, consistency_level);
}
catch (RequestValidationException e)
@@ -410,7 +412,7 @@ public class CassandraServer implements Cassandra.Iface
throws RequestValidationException, 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);
@@ -485,7 +487,7 @@ public class CassandraServer implements Cassandra.Iface
try
{
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);
@@ -586,7 +588,7 @@ public class CassandraServer implements Cassandra.Iface
try
{
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>();
@@ -613,7 +615,7 @@ public class CassandraServer implements Cassandra.Iface
throws RequestValidationException, 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);
@@ -695,7 +697,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);
}
@@ -814,7 +816,7 @@ public class CassandraServer implements Cassandra.Iface
throws RequestValidationException, 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);
@@ -897,7 +899,7 @@ public class CassandraServer implements Cassandra.Iface
{
try
{
- state().hasKeyspaceSchemaAccess(Permission.READ);
+ state().hasKeyspaceAccess(table, Permission.USE);
KSMetaData ksm = Schema.instance.getTableDefinition(table);
if (ksm == null)
@@ -914,7 +916,6 @@ public class CassandraServer implements Cassandra.Iface
public List<KeySlice> get_range_slices(ColumnParent column_parent, SlicePredicate predicate, KeyRange range, ConsistencyLevel consistency_level)
throws InvalidRequestException, UnavailableException, TException, TimedOutException
{
-
if (startSessionIfRequested())
{
Map<String, String> traceParameters = ImmutableMap.of(
@@ -937,7 +938,7 @@ public class CassandraServer implements Cassandra.Iface
ClientState cState = state();
keyspace = cState.getKeyspace();
- cState.hasColumnFamilyAccess(column_parent.column_family, Permission.READ);
+ cState.hasColumnFamilyAccess(column_parent.column_family, Permission.SELECT);
metadata = ThriftValidation.validateColumnFamily(keyspace, column_parent.column_family);
ThriftValidation.validateColumnParent(metadata, column_parent);
@@ -1024,7 +1025,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);
@@ -1125,7 +1126,7 @@ public class CassandraServer implements Cassandra.Iface
try
{
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);
@@ -1178,16 +1179,20 @@ public class CassandraServer implements Cassandra.Iface
{
try
{
- state().hasKeyspaceSchemaAccess(Permission.READ);
-
Set<String> keyspaces = Schema.instance.getTables();
List<KsDef> ksset = new ArrayList<KsDef>(keyspaces.size());
for (String ks : keyspaces)
{
try
{
+ state().hasKeyspaceAccess(ks, Permission.USE);
ksset.add(describe_keyspace(ks));
}
+ catch (UnauthorizedException e)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("PermissionDenied: " + e.getMessage());
+ }
catch (NotFoundException nfe)
{
logger.info("Failed to find metadata for keyspace '" + ks + "'. Continuing... ");
@@ -1296,7 +1301,7 @@ public class CassandraServer implements Cassandra.Iface
try
{
- state().hasColumnFamilySchemaAccess(Permission.WRITE);
+ state().hasColumnFamilyAccess(cf_def.name, Permission.CREATE);
cf_def.unsetId(); // explicitly ignore any id set by client (Hector likes to set zero)
CFMetaData cfm = CFMetaData.fromThrift(cf_def);
if (cfm.getBloomFilterFpChance() == null)
@@ -1320,7 +1325,7 @@ public class CassandraServer implements Cassandra.Iface
try
{
- cState.hasColumnFamilySchemaAccess(Permission.WRITE);
+ cState.hasColumnFamilyAccess(column_family, Permission.DROP);
MigrationManager.announceColumnFamilyDrop(cState.getKeyspace(), column_family);
return Schema.instance.getVersion().toString();
}
@@ -1334,10 +1339,11 @@ public class CassandraServer implements Cassandra.Iface
throws InvalidRequestException, SchemaDisagreementException, TException
{
logger.debug("add_keyspace");
+
try
{
ThriftValidation.validateKeyspaceNotSystem(ks_def.name);
- state().hasKeyspaceSchemaAccess(Permission.WRITE);
+ state().hasKeyspaceAccess(ks_def.name, Permission.CREATE);
ThriftValidation.validateKeyspaceNotYetExisting(ks_def.name);
// generate a meaningful error if the user setup keyspace and/or column definition incorrectly
@@ -1370,10 +1376,11 @@ public class CassandraServer implements Cassandra.Iface
throws InvalidRequestException, SchemaDisagreementException, TException
{
logger.debug("drop_keyspace");
+
try
{
ThriftValidation.validateKeyspaceNotSystem(keyspace);
- state().hasKeyspaceSchemaAccess(Permission.WRITE);
+ state().hasKeyspaceAccess(keyspace, Permission.DROP);
MigrationManager.announceKeyspaceDrop(keyspace);
return Schema.instance.getVersion().toString();
@@ -1391,10 +1398,11 @@ public class CassandraServer implements Cassandra.Iface
throws InvalidRequestException, SchemaDisagreementException, TException
{
logger.debug("update_keyspace");
+
try
{
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.");
@@ -1412,15 +1420,17 @@ public class CassandraServer implements Cassandra.Iface
throws InvalidRequestException, SchemaDisagreementException, TException
{
logger.debug("update_column_family");
+
try
{
- state().hasColumnFamilySchemaAccess(Permission.WRITE);
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);
if (oldCfm == null)
throw new InvalidRequestException("Could not find column family definition to modify.");
+ state().hasColumnFamilyAccess(cf_def.name, Permission.ALTER);
+
CFMetaData.applyImplicitDefaults(cf_def);
CFMetaData cfm = CFMetaData.fromThrift(cf_def);
cfm.addDefaultIndexNames();
@@ -1439,7 +1449,7 @@ public class CassandraServer implements Cassandra.Iface
try
{
- cState.hasColumnFamilyAccess(cfname, Permission.WRITE);
+ cState.hasColumnFamilyAccess(cfname, Permission.DELETE);
if (startSessionIfRequested())
{
@@ -1523,7 +1533,7 @@ public class CassandraServer implements Cassandra.Iface
try
{
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);