mirror of https://github.com/apache/cassandra
Replace IAuthority with new IAuthorizer; patch by Aleksey Yeschenko,
reviewed by Jonathan Ellis for CASSANDRA-4874
This commit is contained in:
parent
7b4639ab26
commit
5a3eb1a6c3
|
|
@ -25,6 +25,7 @@
|
|||
* Fix adding column when the table has collections (CASSANDRA-4982)
|
||||
* Fix allowing collections with compact storage (CASSANDRA-4990)
|
||||
* Refuse ttl/writetime function on collections (CASSANDRA-4992)
|
||||
* Replace IAuthority with new IAuthorizer (CASSANDRA-4874)
|
||||
Merged from 1.1:
|
||||
* cqlsh: improve COPY FROM performance (CASSANDRA-4921)
|
||||
* Fall back to old describe_splits if d_s_ex is not available (CASSANDRA-4803)
|
||||
|
|
|
|||
14
NEWS.txt
14
NEWS.txt
|
|
@ -14,6 +14,15 @@ by version X, but the inverse is not necessarily the case.)
|
|||
|
||||
Upgrading
|
||||
---------
|
||||
- IAuthority interface has been deprecated in favor of IAuthorizer.
|
||||
AllowAllAuthority and SimpleAuthority have been renamed to
|
||||
AllowAllAuthorizer and SimpleAuthorizer, respectively. In order to
|
||||
simplify the upgrade to the new interface, a new abstract
|
||||
LegacyAuthorizer has been added - you should subclass it in your
|
||||
old IAuthority implementation and everything should just work
|
||||
(this only affects users who implemented custom authorities).
|
||||
'authority' setting in cassandra.yaml has been renamed to 'authorizer',
|
||||
'authority' is no longer recognized. This affects all upgrading users.
|
||||
- 1.2 is NOT network-compatible with versions older than 1.0. That
|
||||
means if you want to do a rolling, zero-downtime upgrade, you'll need
|
||||
to upgrade first to 1.0.x or 1.1.x, and then to 1.2. 1.2 retains
|
||||
|
|
@ -93,6 +102,11 @@ Features
|
|||
guarantee that (at the price of pre-writing the batch to another node
|
||||
first), all mutations in the batch will be applied, even if the
|
||||
coordinator fails mid-batch.
|
||||
- new IAuthorizer interface has replaced the old IAuthority. IAuthorizer
|
||||
allows dynamic permission management via new CQL3 statements:
|
||||
GRANT, REVOKE, LIST PERMISSIONS. A native implementation storing
|
||||
the permissions in Cassandra is being worked on and we expect to
|
||||
include it in 1.2.1 or 1.2.2.
|
||||
|
||||
|
||||
1.1.5
|
||||
|
|
|
|||
|
|
@ -56,8 +56,8 @@ max_hints_delivery_threads: 2
|
|||
# authentication backend, implementing IAuthenticator; used to identify users
|
||||
authenticator: org.apache.cassandra.auth.AllowAllAuthenticator
|
||||
|
||||
# authorization backend, implementing IAuthority; used to limit access/provide permissions
|
||||
authority: org.apache.cassandra.auth.AllowAllAuthority
|
||||
# authorization backend, implementing IAuthorizer; used to limit access/provide permissions
|
||||
authorizer: org.apache.cassandra.auth.AllowAllAuthorizer
|
||||
|
||||
# The partitioner is responsible for distributing rows (by key) across
|
||||
# nodes in the cluster. Any IPartitioner may be used, including your
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ hinted_handoff_throttle_delay_in_ms: 50
|
|||
# authentication backend, implementing IAuthenticator; used to identify users
|
||||
authenticator: org.apache.cassandra.auth.AllowAllAuthenticator
|
||||
|
||||
# authorization backend, implementing IAuthority; used to limit access/provide permissions
|
||||
authority: org.apache.cassandra.auth.AllowAllAuthority
|
||||
# authorization backend, implementing IAuthorizer; used to limit access/provide permissions
|
||||
authorizer: org.apache.cassandra.auth.AllowAllAuthorizer
|
||||
|
||||
# The partitioner is responsible for distributing rows (by key) across
|
||||
# nodes in the cluster. Any IPartitioner may be used, including your
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
The files in this directory provide a (simplistic) example of how to add
|
||||
authentication and resource permissions to Cassandra by implementing the
|
||||
org.apache.cassandra.auth.{IAuthenticator, IAuthority} interfaces.
|
||||
org.apache.cassandra.auth.{IAuthenticator, IAuthorizer} interfaces.
|
||||
|
||||
To try those examples, copy the two JAVA sources (in src/) into the main
|
||||
cassandra sources directory and the two configuration files (in conf/) in the
|
||||
main cassandra configuration directory.
|
||||
|
||||
You can then set the authenticator and authority properties in cassandra.yaml
|
||||
You can then set the authenticator and authorizer properties in cassandra.yaml
|
||||
to use those classes. See the two configuration files access.properties and
|
||||
passwd.properties to configure the authorized users and permissions.
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This is a sample access file for SimpleAuthority. The format of this file
|
||||
# This is a sample access file for SimpleAuthorizer. The format of this file
|
||||
# is KEYSPACE[.COLUMNFAMILY].PERMISSION=USERS, where:
|
||||
#
|
||||
# * KEYSPACE is the keyspace name.
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import java.util.Properties;
|
|||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
import org.apache.cassandra.io.util.FileUtils;
|
||||
|
||||
public class SimpleAuthority implements IAuthority
|
||||
public class SimpleAuthorizer extends LegacyAuthorizer
|
||||
{
|
||||
public final static String ACCESS_FILENAME_PROPERTY = "access.properties";
|
||||
// magical property for WRITE permissions to the keyspaces list
|
||||
|
|
@ -41,10 +41,10 @@ public class SimpleAuthority implements IAuthority
|
|||
public EnumSet<Permission> authorize(AuthenticatedUser user, List<Object> resource)
|
||||
{
|
||||
if (resource.size() < 2 || !Resources.ROOT.equals(resource.get(0)) || !Resources.KEYSPACES.equals(resource.get(1)))
|
||||
return Permission.NONE;
|
||||
return EnumSet.copyOf(Permission.NONE);
|
||||
|
||||
String keyspace, columnFamily = null;
|
||||
EnumSet<Permission> authorized = Permission.NONE;
|
||||
EnumSet<Permission> authorized = EnumSet.copyOf(Permission.NONE);
|
||||
|
||||
// /cassandra/keyspaces
|
||||
if (resource.size() == 2)
|
||||
|
|
@ -83,7 +83,7 @@ public class SimpleAuthority implements IAuthority
|
|||
String kspAdmins = accessProperties.getProperty(KEYSPACES_WRITE_PROPERTY);
|
||||
for (String admin : kspAdmins.split(","))
|
||||
if (admin.equals(user.username))
|
||||
return Permission.ALL;
|
||||
return EnumSet.copyOf(Permission.ALL);
|
||||
}
|
||||
|
||||
boolean canRead = false, canWrite = false;
|
||||
|
|
@ -125,7 +125,7 @@ public class SimpleAuthority implements IAuthority
|
|||
}
|
||||
|
||||
if (canWrite)
|
||||
authorized = Permission.ALL;
|
||||
authorized = EnumSet.copyOf(Permission.ALL);
|
||||
else if (canRead)
|
||||
authorized = EnumSet.of(Permission.READ);
|
||||
|
||||
|
|
@ -43,14 +43,12 @@ SYSTEM_KEYSPACES = ('system', 'system_traces')
|
|||
class Cql3ParsingRuleSet(CqlParsingRuleSet):
|
||||
keywords = set((
|
||||
'select', 'from', 'where', 'and', 'key', 'insert', 'update', 'with',
|
||||
'limit', 'using', 'consistency', 'one', 'quorum', 'all', 'any',
|
||||
'local_quorum', 'each_quorum', 'two', 'three', 'use', 'count', 'set',
|
||||
'limit', 'using', 'use', 'count', 'set',
|
||||
'begin', 'apply', 'batch', 'truncate', 'delete', 'in', 'create',
|
||||
'keyspace', 'schema', 'columnfamily', 'table', 'index', 'on', 'drop',
|
||||
'primary', 'into', 'values', 'timestamp', 'ttl', 'alter', 'add', 'type',
|
||||
'compact', 'storage', 'order', 'by', 'asc', 'desc', 'clustering',
|
||||
'token', 'writetime', 'map', 'list', 'to', 'grant', 'grants', 'revoke',
|
||||
'option', 'describe', 'for', 'full_access', 'no_access'
|
||||
'token', 'writetime', 'map', 'list', 'to'
|
||||
))
|
||||
|
||||
columnfamily_options = (
|
||||
|
|
@ -275,16 +273,6 @@ JUNK ::= /([ \t\r\f\v]+|(--|[/][/])[^\n\r]*([\n\r]|$)|[/][*].*?[*][/])/ ;
|
|||
| <alterKeyspaceStatement>
|
||||
;
|
||||
|
||||
<consistencylevel> ::= cl=( <K_ONE>
|
||||
| <K_QUORUM>
|
||||
| <K_ALL>
|
||||
| <K_ANY>
|
||||
| <K_LOCAL_QUORUM>
|
||||
| <K_EACH_QUORUM>
|
||||
| <K_TWO>
|
||||
| <K_THREE> )
|
||||
;
|
||||
|
||||
# timestamp is included here, since it's also a keyword
|
||||
<simpleStorageType> ::= typename=( <identifier> | <stringLiteral> | <K_TIMESTAMP> ) ;
|
||||
|
||||
|
|
@ -307,15 +295,13 @@ JUNK ::= /([ \t\r\f\v]+|(--|[/][/])[^\n\r]*([\n\r]|$)|[/][*].*?[*][/])/ ;
|
|||
|
||||
<unreservedKeyword> ::= nocomplete=
|
||||
( <K_KEY>
|
||||
| <K_CONSISTENCY>
|
||||
| <K_CLUSTERING>
|
||||
# | <K_COUNT> -- to get count(*) completion, treat count as reserved
|
||||
| <K_TTL>
|
||||
| <K_COMPACT>
|
||||
| <K_STORAGE>
|
||||
| <K_TYPE>
|
||||
| <K_VALUES>
|
||||
| <consistencylevel> )
|
||||
| <K_VALUES> )
|
||||
;
|
||||
|
||||
# <property> will be defined once cqlsh determines whether we're using
|
||||
|
|
@ -666,10 +652,6 @@ def cf_old_prop_suboption_completer(ctxt, cass):
|
|||
return subopts
|
||||
return ()
|
||||
|
||||
@completer_for('consistencylevel', 'cl')
|
||||
def consistencylevel_cl_completer(ctxt, cass):
|
||||
return CqlRuleSet.consistency_levels
|
||||
|
||||
@completer_for('tokenDefinition', 'token')
|
||||
def token_word_completer(ctxt, cass):
|
||||
return ['TOKEN(']
|
||||
|
|
@ -1246,39 +1228,48 @@ syntax_rules += r'''
|
|||
<alterKeyspaceStatement> ::= "ALTER" ( "KEYSPACE" | "SCHEMA" ) ks=<nonSystemKeyspaceName>
|
||||
"WITH" <newPropSpec> ( "AND" <newPropSpec> )*
|
||||
;
|
||||
'''
|
||||
|
||||
<grantStatement> ::= "GRANT" <permission> "ON" cf=<columnFamilyName>
|
||||
"TO" <username>
|
||||
( "WITH" "GRANT" "OPTION" )?
|
||||
syntax_rules += r'''
|
||||
<grantStatement> ::= "GRANT" <permissionExpr> "ON" <resource> "TO" <username>
|
||||
;
|
||||
|
||||
<revokeStatement> ::= "REVOKE" <permission> "ON" cf=<columnFamilyName>
|
||||
"FROM" <username>
|
||||
<revokeStatement> ::= "REVOKE" <permissionExpr> "ON" <resource> "FROM" <username>
|
||||
;
|
||||
|
||||
<listGrantsStatement> ::= "LIST" "GRANTS" "FOR" <username>;
|
||||
<listGrantsStatement> ::= "LIST" <permissionExpr>
|
||||
( "ON" <resource> )? ( "OF" <username> )? "NORECURSIVE"?
|
||||
;
|
||||
|
||||
<permission> ::= "DESCRIBE"
|
||||
| "USE"
|
||||
<permission> ::= "AUTHORIZE"
|
||||
| "CREATE"
|
||||
| "ALTER"
|
||||
| "DROP"
|
||||
| "SELECT"
|
||||
| "INSERT"
|
||||
| "UPDATE"
|
||||
| "DELETE"
|
||||
| "FULL_ACCESS"
|
||||
| "NO_ACCESS"
|
||||
| "MODIFY"
|
||||
;
|
||||
|
||||
<permissionExpr> ::= ( <permission> "PERMISSION"? )
|
||||
| ( "ALL" "PERMISSIONS"? )
|
||||
;
|
||||
|
||||
<resource> ::= <dataResource>
|
||||
;
|
||||
|
||||
<dataResource> ::= ( "ALL" "KEYSPACES" )
|
||||
| ( "KEYSPACE" <nonSystemKeyspaceName> )
|
||||
| ( "TABLE"? <columnFamilyName> )
|
||||
;
|
||||
|
||||
<username> ::= user=( <identifier> | <stringLiteral> )
|
||||
;
|
||||
'''
|
||||
|
||||
@completer_for('username', 'user')
|
||||
def username_user_completer(ctxt, cass):
|
||||
# TODO: implement user autocompletion
|
||||
# with I could see a way to do this usefully, but I don't. I don't know
|
||||
# how any Authorities other than AllowAllAuthority work :/
|
||||
# how any Authorities other than AllowAllAuthorizer work :/
|
||||
return [Hint('<username>')]
|
||||
|
||||
# END SYNTAX/COMPLETION RULE DEFINITIONS
|
||||
|
|
|
|||
|
|
@ -24,16 +24,20 @@ import org.apache.cassandra.thrift.AuthenticationException;
|
|||
|
||||
public class AllowAllAuthenticator implements IAuthenticator
|
||||
{
|
||||
private final static AuthenticatedUser USER = new AuthenticatedUser("allow_all");
|
||||
private final static AuthenticatedUser DEFAULT_USER = new AuthenticatedUser("nobody");
|
||||
|
||||
public AuthenticatedUser defaultUser()
|
||||
{
|
||||
return USER;
|
||||
return DEFAULT_USER;
|
||||
}
|
||||
|
||||
public AuthenticatedUser authenticate(Map<? extends CharSequence,? extends CharSequence> credentials) throws AuthenticationException
|
||||
{
|
||||
return USER;
|
||||
|
||||
CharSequence username = credentials.get(IAuthenticator.USERNAME_KEY);
|
||||
if (username == null)
|
||||
return DEFAULT_USER;
|
||||
return new AuthenticatedUser((String)username);
|
||||
}
|
||||
|
||||
public void validateConfiguration() throws ConfigurationException
|
||||
|
|
|
|||
|
|
@ -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 java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
|
||||
public class AllowAllAuthorizer implements IAuthorizer
|
||||
{
|
||||
public Set<Permission> authorize(AuthenticatedUser user, IResource resource)
|
||||
{
|
||||
return Permission.ALL;
|
||||
}
|
||||
|
||||
public void validateConfiguration()
|
||||
{
|
||||
}
|
||||
|
||||
public void setup()
|
||||
{
|
||||
}
|
||||
|
||||
public void grant(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String to)
|
||||
throws InvalidRequestException
|
||||
{
|
||||
throw new InvalidRequestException("GRANT operation is not supported by AllowAllAuthorizer");
|
||||
}
|
||||
|
||||
public void revoke(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String from)
|
||||
throws InvalidRequestException
|
||||
{
|
||||
throw new InvalidRequestException("REVOKE operation is not supported by AllowAllAuthorizer");
|
||||
}
|
||||
|
||||
public void revokeAll(String droppedUser)
|
||||
{
|
||||
}
|
||||
|
||||
public void revokeAll(IResource droppedResource)
|
||||
{
|
||||
}
|
||||
|
||||
public Set<PermissionDetails> listPermissions(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String of)
|
||||
throws InvalidRequestException
|
||||
{
|
||||
throw new InvalidRequestException("LIST PERMISSIONS operation is not supported by AllowAllAuthorizer");
|
||||
}
|
||||
|
||||
public Set<IResource> protectedResources()
|
||||
{
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
|
@ -17,32 +17,30 @@
|
|||
*/
|
||||
package org.apache.cassandra.auth;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An authenticated user and her groups.
|
||||
*/
|
||||
public class AuthenticatedUser
|
||||
{
|
||||
public final String username;
|
||||
public final Set<String> groups;
|
||||
private final boolean isSuperUser;
|
||||
|
||||
public AuthenticatedUser(String username)
|
||||
{
|
||||
this.username = username;
|
||||
this.groups = Collections.emptySet();
|
||||
this(username, false);
|
||||
}
|
||||
|
||||
public AuthenticatedUser(String username, Set<String> groups)
|
||||
public AuthenticatedUser(String username, boolean isSuperUser)
|
||||
{
|
||||
this.username = username;
|
||||
this.groups = Collections.unmodifiableSet(groups);
|
||||
this.isSuperUser = isSuperUser;
|
||||
}
|
||||
|
||||
public boolean isSuperUser()
|
||||
{
|
||||
return isSuperUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("#<User %s groups=%s>", username, groups);
|
||||
return String.format("#<User %s super=%s>", username, isSuperUser);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* 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 com.google.common.base.Objects;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import org.apache.cassandra.config.Schema;
|
||||
|
||||
/**
|
||||
* The primary type of resource in Cassandra.
|
||||
*
|
||||
* Used to represent a column family or a keyspace or the root level "data" resource.
|
||||
* "data" - the root level data resource.
|
||||
* "data/keyspace_name" - keyspace-level data resource.
|
||||
* "data/keyspace_name/column_family_name" - cf-level data resource.
|
||||
*/
|
||||
public class DataResource implements IResource
|
||||
{
|
||||
enum Level
|
||||
{
|
||||
ROOT, KEYSPACE, COLUMN_FAMILY
|
||||
}
|
||||
|
||||
private static final String ROOT_NAME = "data";
|
||||
private static final DataResource ROOT_RESOURCE = new DataResource();
|
||||
|
||||
private final Level level;
|
||||
private final String keyspace;
|
||||
private final String columnFamily;
|
||||
|
||||
private DataResource()
|
||||
{
|
||||
level = Level.ROOT;
|
||||
keyspace = null;
|
||||
columnFamily = null;
|
||||
}
|
||||
|
||||
private DataResource(String keyspace)
|
||||
{
|
||||
level = Level.KEYSPACE;
|
||||
this.keyspace = keyspace;
|
||||
columnFamily = null;
|
||||
}
|
||||
|
||||
private DataResource(String keyspace, String columnFamily)
|
||||
{
|
||||
level = Level.COLUMN_FAMILY;
|
||||
this.keyspace = keyspace;
|
||||
this.columnFamily = columnFamily;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the root-level resource.
|
||||
*/
|
||||
public static DataResource root()
|
||||
{
|
||||
return ROOT_RESOURCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DataResource representing a keyspace.
|
||||
*
|
||||
* @param keyspace Name of the keyspace.
|
||||
* @return DataResource instance representing the keyspace.
|
||||
*/
|
||||
public static DataResource keyspace(String keyspace)
|
||||
{
|
||||
return new DataResource(keyspace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a DataResource instance representing a column family.
|
||||
*
|
||||
* @param keyspace Name of the keyspace.
|
||||
* @param columnFamily Name of the column family.
|
||||
* @return DataResource instance representing the column family.
|
||||
*/
|
||||
public static DataResource columnFamily(String keyspace, String columnFamily)
|
||||
{
|
||||
return new DataResource(keyspace, columnFamily);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a data resource name into a DataResource instance.
|
||||
*
|
||||
* @param name Name of the data resource.
|
||||
* @return DataResource instance matching the name.
|
||||
*/
|
||||
public static DataResource fromName(String name)
|
||||
{
|
||||
String[] parts = StringUtils.split(name, '/');
|
||||
|
||||
if (!parts[0].equals(ROOT_NAME) || parts.length > 3)
|
||||
throw new IllegalArgumentException(String.format("%s is not a valid data resource name", name));
|
||||
|
||||
if (parts.length == 1)
|
||||
return root();
|
||||
|
||||
if (parts.length == 2)
|
||||
return keyspace(parts[1]);
|
||||
|
||||
return columnFamily(parts[1], parts[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Printable name of the resource.
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case ROOT:
|
||||
return ROOT_NAME;
|
||||
case KEYSPACE:
|
||||
return String.format("%s/%s", ROOT_NAME, keyspace);
|
||||
case COLUMN_FAMILY:
|
||||
return String.format("%s/%s/%s", ROOT_NAME, keyspace, columnFamily);
|
||||
}
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Parent of the resource, if any. Throws IllegalStateException if it's the root-level resource.
|
||||
*/
|
||||
public IResource getParent()
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case KEYSPACE:
|
||||
return root();
|
||||
case COLUMN_FAMILY:
|
||||
return keyspace(keyspace);
|
||||
}
|
||||
throw new IllegalStateException("Root-level resource can't have a parent");
|
||||
}
|
||||
|
||||
public boolean isRootLevel()
|
||||
{
|
||||
return level.equals(Level.ROOT);
|
||||
}
|
||||
|
||||
public boolean isKeyspaceLevel()
|
||||
{
|
||||
return level.equals(Level.KEYSPACE);
|
||||
}
|
||||
|
||||
public boolean isColumnFamilyLevel()
|
||||
{
|
||||
return level.equals(Level.COLUMN_FAMILY);
|
||||
}
|
||||
/**
|
||||
* @return keyspace of the resource. Throws IllegalStateException if it's the root-level resource.
|
||||
*/
|
||||
public String getKeyspace()
|
||||
{
|
||||
if (isRootLevel())
|
||||
throw new IllegalStateException("ROOT data resource has no keyspace");
|
||||
return keyspace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return column family of the resource. Throws IllegalStateException if it's not a cf-level resource.
|
||||
*/
|
||||
public String getColumnFamily()
|
||||
{
|
||||
if (!isColumnFamilyLevel())
|
||||
throw new IllegalStateException(String.format("%s data resource has no column family", level));
|
||||
return columnFamily;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether or not the resource has a parent in the hierarchy.
|
||||
*/
|
||||
public boolean hasParent()
|
||||
{
|
||||
return !level.equals(Level.ROOT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether or not the resource exists in Cassandra.
|
||||
*/
|
||||
public boolean exists()
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case ROOT:
|
||||
return true;
|
||||
case KEYSPACE:
|
||||
return Schema.instance.getTables().contains(keyspace);
|
||||
case COLUMN_FAMILY:
|
||||
return Schema.instance.getCFMetaData(keyspace, columnFamily) != null;
|
||||
}
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case ROOT:
|
||||
return "<all keyspaces>";
|
||||
case KEYSPACE:
|
||||
return String.format("<keyspace %s>", keyspace);
|
||||
case COLUMN_FAMILY:
|
||||
return String.format("<table %s.%s>", keyspace, columnFamily);
|
||||
}
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (this == o)
|
||||
return true;
|
||||
|
||||
if (!(o instanceof DataResource))
|
||||
return false;
|
||||
|
||||
DataResource ds = (DataResource) o;
|
||||
|
||||
return Objects.equal(this.level, ds.level)
|
||||
&& Objects.equal(this.keyspace, ds.keyspace)
|
||||
&& Objects.equal(this.columnFamily, ds.columnFamily);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(level, keyspace, columnFamily);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.cassandra.auth;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
|
||||
/**
|
||||
*
|
||||
* Cassandra's resource hierarchy looks something like:
|
||||
* {{/cassandra/keyspaces/$ks_name/...}}
|
||||
*
|
||||
* In table form:
|
||||
* /cassandra/
|
||||
* - no checked permissions
|
||||
* - String
|
||||
* * Separates Cassandra-internal resources from resources that might be provided by plugins.
|
||||
* keyspaces/
|
||||
* - READ, WRITE
|
||||
* - String
|
||||
* * The list of keyspaces: READ/WRITE for this resource mean the ability to view/modify the list of keyspaces.
|
||||
* $ks_name/
|
||||
* - READ, WRITE
|
||||
* - String
|
||||
* * An individual keyspace: READ/WRITE permissions apply to the entire namespace and control the ability to both
|
||||
* view and manipulate column families, and to read and write the data contained within.
|
||||
*
|
||||
* Over time Cassandra _may_ add additional authorize calls for resources higher or lower in the hierarchy and
|
||||
* IAuthority implementations should be able to handle these calls (although many will choose to ignore them
|
||||
* completely).
|
||||
*
|
||||
* NB: {{/cassandra/}} will not be checked for permissions via a call to IAuthority.authorize, so IAuthority
|
||||
* implementations can only deny access when a user attempts to access an ancestor resource.
|
||||
*/
|
||||
public interface IAuthority
|
||||
{
|
||||
/**
|
||||
* @param user An authenticated user from a previous call to IAuthenticator.authenticate.
|
||||
* @param resource A List of Objects containing Strings and byte[]s: represents a resource in the hierarchy
|
||||
* described in the Javadocs.
|
||||
* @return An AccessLevel representing the permissions for the user and resource: should never return null.
|
||||
*/
|
||||
public EnumSet<Permission> authorize(AuthenticatedUser user, List<Object> resource);
|
||||
|
||||
public void validateConfiguration() throws ConfigurationException;
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.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;
|
||||
}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
*/
|
||||
package org.apache.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;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* 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 java.util.Set;
|
||||
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.exceptions.UnauthorizedException;
|
||||
|
||||
/**
|
||||
* Primary Cassandra authorization interface.
|
||||
*/
|
||||
public interface IAuthorizer
|
||||
{
|
||||
/**
|
||||
* The primary IAuthorizer method. Returns a set of permissions of a user on a resource.
|
||||
*
|
||||
* @param user Authenticated user requesting authorization.
|
||||
* @param resource Resource for which the authorization is being requested. @see DataResource.
|
||||
* @return Set of permissions of the user on the resource. Should never return null. Use Permission.NONE instead.
|
||||
*/
|
||||
public Set<Permission> authorize(AuthenticatedUser user, IResource resource);
|
||||
|
||||
/**
|
||||
* Grants a set of permissions on a resource to a user.
|
||||
* The opposite of revoke().
|
||||
*
|
||||
* @param performer User who grants the permissions.
|
||||
* @param permissions Set of permissions to grant.
|
||||
* @param to Grantee of the permissions.
|
||||
* @param resource Resource on which to grant the permissions.
|
||||
*
|
||||
* @throws UnauthorizedException if the granting user isn't allowed to grant (and revoke) the permissions on the resource.
|
||||
* @throws InvalidRequestException upon parameter misconfiguration or internal error.
|
||||
*/
|
||||
public void grant(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String to)
|
||||
throws UnauthorizedException, InvalidRequestException;
|
||||
|
||||
/**
|
||||
* Revokes a set of permissions on a resource from a user.
|
||||
* The opposite of grant().
|
||||
*
|
||||
* @param performer User who revokes the permissions.
|
||||
* @param permissions Set of permissions to revoke.
|
||||
* @param from Revokee of the permissions.
|
||||
* @param resource Resource on which to revoke the permissions.
|
||||
*
|
||||
* @throws UnauthorizedException if the revoking user isn't allowed to revoke the permissions on the resource.
|
||||
* @throws InvalidRequestException upon parameter misconfiguration or internal error.
|
||||
*/
|
||||
public void revoke(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String from)
|
||||
throws UnauthorizedException, InvalidRequestException;
|
||||
|
||||
/**
|
||||
* Returns a list of permissions on a resource of a user.
|
||||
*
|
||||
* @param performer User who wants to see the permissions.
|
||||
* @param permissions Set of Permission values the user is interested in. The result should only include the matching ones.
|
||||
* @param resource The resource on which permissions are requested. Can be null, in which case permissions on all resources
|
||||
* should be returned.
|
||||
* @param of The user whose permissions are requested. Can be null, in which case permissions of every user should be returned.
|
||||
*
|
||||
* @return All of the matching permission that the requesting user is authorized to know about.
|
||||
*
|
||||
* @throws UnauthorizedException if the user isn't allowed to view the requested permissions.
|
||||
* @throws InvalidRequestException upon parameter misconfiguration or internal error.
|
||||
*/
|
||||
public Set<PermissionDetails> listPermissions(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String of)
|
||||
throws UnauthorizedException, InvalidRequestException;
|
||||
|
||||
/**
|
||||
* This method is called before deleting a user with DROP USER query so that a new user with the same
|
||||
* name wouldn't inherit permissions of the deleted user in the future.
|
||||
*
|
||||
* @param droppedUser The user to revoke all permissions from.
|
||||
*/
|
||||
public void revokeAll(String droppedUser);
|
||||
|
||||
/**
|
||||
* This method is called after a resource is removed (i.e. keyspace or a table is dropped).
|
||||
*
|
||||
* @param droppedResource The resource to revoke all permissions on.
|
||||
*/
|
||||
public void revokeAll(IResource droppedResource);
|
||||
|
||||
/**
|
||||
* Set of resources that should be made inaccessible to users and only accessible internally.
|
||||
*
|
||||
* @return Keyspaces, column families that will be unreadable and unmodifiable by users; other resources.
|
||||
*/
|
||||
public Set<? extends IResource> protectedResources();
|
||||
|
||||
/**
|
||||
* Validates configuration of IAuthorizer implementation (if configurable).
|
||||
*
|
||||
* @throws ConfigurationException when there is a configuration error.
|
||||
*/
|
||||
public void validateConfiguration() throws ConfigurationException;
|
||||
|
||||
/**
|
||||
* Setup is called once upon system startup to initialize the IAuthorizer.
|
||||
*
|
||||
* For example, use this method to create any required keyspaces/column families.
|
||||
*/
|
||||
public void setup();
|
||||
}
|
||||
|
|
@ -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.auth;
|
||||
|
||||
/**
|
||||
* The interface at the core of Cassandra authorization.
|
||||
*
|
||||
* Represents a resource in the hierarchy.
|
||||
* Currently just one resource type is supported by Cassandra
|
||||
* @see DataResource
|
||||
*/
|
||||
public interface IResource
|
||||
{
|
||||
/**
|
||||
* @return printable name of the resource.
|
||||
*/
|
||||
public String getName();
|
||||
|
||||
/**
|
||||
* Gets next resource in the hierarchy. Call hasParent first to make sure there is one.
|
||||
*
|
||||
* @return Resource parent (or IllegalStateException if there is none). Never a null.
|
||||
*/
|
||||
public IResource getParent();
|
||||
|
||||
/**
|
||||
* Indicates whether or not this resource has a parent in the hierarchy.
|
||||
*
|
||||
* Please perform this check before calling getParent() method.
|
||||
* @return Whether or not the resource has a parent.
|
||||
*/
|
||||
public boolean hasParent();
|
||||
|
||||
/**
|
||||
* @return Whether or not this resource exists in Cassandra.
|
||||
*/
|
||||
public boolean exists();
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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 java.util.*;
|
||||
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.exceptions.UnauthorizedException;
|
||||
|
||||
/**
|
||||
* Provides a transitional IAuthorizer implementation for old-style (pre-1.2) authorizers.
|
||||
*
|
||||
* Translates old-style authorze() calls to the new-style, expands Permission.READ and Permission.WRITE
|
||||
* into the new Permission values, translates the new resource hierarchy into the old hierarchy.
|
||||
* Stubs the rest of the new methods.
|
||||
* Subclass LegacyAuthorizer instead of implementing the old IAuthority and your old IAuthority implementation should
|
||||
* continue to work.
|
||||
*/
|
||||
public abstract class LegacyAuthorizer implements IAuthorizer
|
||||
{
|
||||
/**
|
||||
* @param user Authenticated user requesting authorization.
|
||||
* @param resource List of Objects containing Strings and byte[]s: represents a resource in the old hierarchy.
|
||||
* @return Set of permissions of the user on the resource. Should never return null. Use Permission.NONE instead.
|
||||
*/
|
||||
public abstract EnumSet<Permission> authorize(AuthenticatedUser user, List<Object> resource);
|
||||
|
||||
@Override
|
||||
public void setup()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates new-style authorize() method call to the old-style (including permissions and the hierarchy).
|
||||
*/
|
||||
@Override
|
||||
public Set<Permission> authorize(AuthenticatedUser user, IResource resource)
|
||||
{
|
||||
if (!(resource instanceof DataResource))
|
||||
throw new IllegalArgumentException(String.format("%s resource is not supported by LegacyAuthorizer", resource.getName()));
|
||||
DataResource dr = (DataResource) resource;
|
||||
|
||||
List<Object> legacyResource = new ArrayList<Object>();
|
||||
legacyResource.add(Resources.ROOT);
|
||||
legacyResource.add(Resources.KEYSPACES);
|
||||
if (!dr.isRootLevel())
|
||||
legacyResource.add(dr.getKeyspace());
|
||||
if (dr.isColumnFamilyLevel())
|
||||
legacyResource.add(dr.getColumnFamily());
|
||||
|
||||
Set<Permission> permissions = authorize(user, legacyResource);
|
||||
if (permissions.contains(Permission.READ))
|
||||
permissions.add(Permission.SELECT);
|
||||
if (permissions.contains(Permission.WRITE))
|
||||
permissions.addAll(EnumSet.of(Permission.CREATE, Permission.ALTER, Permission.DROP, Permission.MODIFY));
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grant(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String to)
|
||||
throws InvalidRequestException, UnauthorizedException
|
||||
{
|
||||
throw new InvalidRequestException("GRANT operation is not supported by LegacyAuthorizer");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revoke(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String from)
|
||||
throws InvalidRequestException, UnauthorizedException
|
||||
{
|
||||
throw new InvalidRequestException("REVOKE operation is not supported by LegacyAuthorizer");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokeAll(String droppedUser)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void revokeAll(IResource droppedResource)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PermissionDetails> listPermissions(AuthenticatedUser performer, Set<Permission> permissions, IResource resource, String of)
|
||||
throws InvalidRequestException, UnauthorizedException
|
||||
{
|
||||
throw new InvalidRequestException("LIST PERMISSIONS operation is not supported by LegacyAuthorizer");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<IResource> protectedResources()
|
||||
{
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
|
@ -17,20 +17,37 @@
|
|||
*/
|
||||
package org.apache.cassandra.auth;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.service.IMigrationListener;
|
||||
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
|
||||
public class AllowAllAuthority implements IAuthority
|
||||
/**
|
||||
* IMigrationListener implementation that cleans up permissions on dropped resources.
|
||||
*/
|
||||
public class MigrationListener implements IMigrationListener
|
||||
{
|
||||
public EnumSet<Permission> authorize(AuthenticatedUser user, List<Object> resource)
|
||||
public void onDropKeyspace(String ksName)
|
||||
{
|
||||
return Permission.ALL;
|
||||
DatabaseDescriptor.getAuthorizer().revokeAll(DataResource.keyspace(ksName));
|
||||
}
|
||||
|
||||
public void validateConfiguration() throws ConfigurationException
|
||||
public void onDropColumnFamly(String ksName, String cfName)
|
||||
{
|
||||
DatabaseDescriptor.getAuthorizer().revokeAll(DataResource.columnFamily(ksName, cfName));
|
||||
}
|
||||
|
||||
public void onCreateKeyspace(String ksName)
|
||||
{
|
||||
}
|
||||
|
||||
public void onCreateColumnFamly(String ksName, String cfName)
|
||||
{
|
||||
}
|
||||
|
||||
public void onUpdateKeyspace(String ksName)
|
||||
{
|
||||
}
|
||||
|
||||
public void onUpdateColumnFamly(String ksName, String cfName)
|
||||
{
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
|
@ -18,45 +18,40 @@
|
|||
package org.apache.cassandra.auth;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/**
|
||||
* An enum encapsulating the set of possible permissions that an authenticated user can have for a resource.
|
||||
* An enum encapsulating the set of possible permissions that an authenticated user can have on a resource.
|
||||
*
|
||||
* IAuthority implementations may encode permissions using ordinals, so the Enum order must never change.
|
||||
* IAuthorizer implementations may encode permissions using ordinals, so the Enum order must never change order.
|
||||
* Adding new values is ok.
|
||||
*/
|
||||
public enum Permission
|
||||
{
|
||||
READ, // for backward compatibility
|
||||
WRITE, // for backward compatibility
|
||||
|
||||
FULL_ACCESS,
|
||||
NO_ACCESS,
|
||||
@Deprecated
|
||||
READ,
|
||||
@Deprecated
|
||||
WRITE,
|
||||
|
||||
// schema management
|
||||
DESCRIBE,
|
||||
CREATE,
|
||||
ALTER,
|
||||
DROP,
|
||||
CREATE, // required for CREATE KEYSPACE and CREATE TABLE.
|
||||
ALTER, // required for ALTER KEYSPACE, ALTER TABLE, CREATE INDEX, DROP INDEX.
|
||||
DROP, // required for DROP KEYSPACE and DROP TABLE.
|
||||
|
||||
// data access
|
||||
UPDATE,
|
||||
DELETE,
|
||||
SELECT;
|
||||
SELECT, // required for SELECT.
|
||||
MODIFY, // required for INSERT, UPDATE, DELETE, TRUNCATE.
|
||||
|
||||
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);
|
||||
public static final EnumSet<Permission> ALLOWED_SYSTEM_ACTIONS = EnumSet.of(DESCRIBE, UPDATE, DELETE, SELECT);
|
||||
// permission management
|
||||
AUTHORIZE; // required for GRANT and REVOKE.
|
||||
|
||||
/**
|
||||
* 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(DESCRIBE, SELECT));
|
||||
put(WRITE, EnumSet.range(DESCRIBE, DELETE));
|
||||
}};
|
||||
|
||||
public static final Set<Permission> ALL_DATA =
|
||||
ImmutableSet.copyOf(EnumSet.range(Permission.CREATE, Permission.AUTHORIZE));
|
||||
|
||||
public static final Set<Permission> ALL =
|
||||
ImmutableSet.copyOf(EnumSet.range(Permission.CREATE, Permission.AUTHORIZE));
|
||||
public static final Set<Permission> NONE = ImmutableSet.of();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cassandra.auth;
|
||||
|
||||
import org.apache.cassandra.thrift.InvalidRequestException;
|
||||
|
||||
public class PermissionDenied extends InvalidRequestException
|
||||
{
|
||||
public PermissionDenied(String reason)
|
||||
{
|
||||
super(reason);
|
||||
}
|
||||
|
||||
public String getMessage()
|
||||
{
|
||||
return why;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 com.google.common.base.Objects;
|
||||
import com.google.common.collect.ComparisonChain;
|
||||
|
||||
/**
|
||||
* Sets of instances of this class are returned by IAuthorizer.listPermissions() method for LIST PERMISSIONS query.
|
||||
* None of the fields are nullable.
|
||||
*/
|
||||
public class PermissionDetails implements Comparable<PermissionDetails>
|
||||
{
|
||||
public final String username;
|
||||
public final IResource resource;
|
||||
public final Permission permission;
|
||||
|
||||
public PermissionDetails(String username, IResource resource, Permission permission)
|
||||
{
|
||||
this.username = username;
|
||||
this.resource = resource;
|
||||
this.permission = permission;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(PermissionDetails other)
|
||||
{
|
||||
return ComparisonChain.start()
|
||||
.compare(username, other.username)
|
||||
.compare(resource.getName(), other.resource.getName())
|
||||
.compare(permission, other.permission)
|
||||
.result();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("<PermissionDetails username:%s resource:%s permission:%s>",
|
||||
username,
|
||||
resource.getName(),
|
||||
permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
return Objects.equal(this, o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(username, resource, permission);
|
||||
}
|
||||
}
|
||||
|
|
@ -17,20 +17,38 @@
|
|||
*/
|
||||
package org.apache.cassandra.auth;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cassandra.utils.Hex;
|
||||
|
||||
/**
|
||||
* Constants related to Cassandra's resource hierarchy.
|
||||
*
|
||||
* A resource in Cassandra is a List containing both Strings and byte[]s.
|
||||
*/
|
||||
public final class Resources
|
||||
{
|
||||
/**
|
||||
* Construct a chain of resource parents starting with the resource and ending with the root.
|
||||
*
|
||||
* @param resource The staring point.
|
||||
* @return list of resource in the chain form start to the root.
|
||||
*/
|
||||
public static List<? extends IResource> chain(IResource resource)
|
||||
{
|
||||
List<IResource> chain = new ArrayList<IResource>();
|
||||
while (true)
|
||||
{
|
||||
chain.add(resource);
|
||||
if (!resource.hasParent())
|
||||
break;
|
||||
resource = resource.getParent();
|
||||
}
|
||||
return chain;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public final static String ROOT = "cassandra";
|
||||
@Deprecated
|
||||
public final static String KEYSPACES = "keyspaces";
|
||||
|
||||
@Deprecated
|
||||
public static String toString(List<Object> resource)
|
||||
{
|
||||
StringBuilder buff = new StringBuilder();
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ public class Config
|
|||
{
|
||||
public String cluster_name = "Test Cluster";
|
||||
public String authenticator;
|
||||
public String authority;
|
||||
public String authority; // for backwards compatibility - will log a warning.
|
||||
public String authorizer;
|
||||
|
||||
/* Hashing strategy Random or OPHF */
|
||||
public String partitioner;
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ import org.apache.cassandra.scheduler.IRequestScheduler;
|
|||
import org.apache.cassandra.scheduler.NoScheduler;
|
||||
import org.apache.cassandra.service.CacheService;
|
||||
import org.apache.cassandra.service.MigrationManager;
|
||||
import org.apache.cassandra.thrift.ThriftServer;
|
||||
import org.apache.cassandra.utils.FBUtilities;
|
||||
import org.yaml.snakeyaml.Loader;
|
||||
import org.yaml.snakeyaml.TypeDescription;
|
||||
|
|
@ -77,8 +76,7 @@ public class DatabaseDescriptor
|
|||
private static Config conf;
|
||||
|
||||
private static IAuthenticator authenticator = new AllowAllAuthenticator();
|
||||
private static IAuthority authority = new AllowAllAuthority();
|
||||
private static IAuthorityContainer authorityContainer;
|
||||
private static IAuthorizer authorizer = new AllowAllAuthorizer();
|
||||
|
||||
private final static String DEFAULT_CONFIGURATION = "cassandra.yaml";
|
||||
|
||||
|
|
@ -206,15 +204,23 @@ public class DatabaseDescriptor
|
|||
|
||||
logger.debug("page_cache_hinting is " + conf.populate_io_cache_on_flush);
|
||||
|
||||
/* Authentication and authorization backend, implementing IAuthenticator and IAuthority */
|
||||
/* Authentication and authorization backend, implementing IAuthenticator and IAuthorizer */
|
||||
if (conf.authenticator != null)
|
||||
authenticator = FBUtilities.<IAuthenticator>construct(conf.authenticator, "authenticator");
|
||||
if (conf.authority != null)
|
||||
authority = FBUtilities.<IAuthority>construct(conf.authority, "authority");
|
||||
authenticator.validateConfiguration();
|
||||
authority.validateConfiguration();
|
||||
|
||||
authorityContainer = new IAuthorityContainer(authority);
|
||||
if (conf.authority != null)
|
||||
{
|
||||
logger.warn("Please rename 'authority' to 'authorizer' in cassandra.yaml");
|
||||
if (!conf.authority.equals("org.apache.cassandra.auth.AllowAllAuthority"))
|
||||
throw new ConfigurationException("IAuthority interface has been deprecated,"
|
||||
+ " please implement IAuthorizer instead.");
|
||||
}
|
||||
|
||||
if (conf.authorizer != null)
|
||||
authorizer = FBUtilities.<IAuthorizer>construct(conf.authorizer, "authorizer");
|
||||
|
||||
authenticator.validateConfiguration();
|
||||
authorizer.validateConfiguration();
|
||||
|
||||
/* Hashing strategy */
|
||||
if (conf.partitioner == null)
|
||||
|
|
@ -462,12 +468,6 @@ public class DatabaseDescriptor
|
|||
Schema.instance.setTableDefinition(ksmd);
|
||||
}
|
||||
|
||||
// setup schema required for authorization
|
||||
authorityContainer.setup();
|
||||
|
||||
// setup schema required for authorization
|
||||
authorityContainer.setup();
|
||||
|
||||
/* Load the seeds for node contact points */
|
||||
if (conf.seed_provider == null)
|
||||
{
|
||||
|
|
@ -585,14 +585,9 @@ public class DatabaseDescriptor
|
|||
return authenticator;
|
||||
}
|
||||
|
||||
public static IAuthority getAuthority()
|
||||
public static IAuthorizer getAuthorizer()
|
||||
{
|
||||
return authority;
|
||||
}
|
||||
|
||||
public static IAuthorityContainer getAuthorityContainer()
|
||||
{
|
||||
return authorityContainer;
|
||||
return authorizer;
|
||||
}
|
||||
|
||||
public static int getThriftMaxMessageLength()
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ public class DeleteStatement extends AbstractModification
|
|||
{
|
||||
CFMetaData metadata = validateColumnFamily(keyspace, columnFamily);
|
||||
|
||||
clientState.hasColumnFamilyAccess(columnFamily, Permission.DELETE);
|
||||
clientState.hasColumnFamilyAccess(keyspace, columnFamily, Permission.MODIFY);
|
||||
AbstractType<?> keyType = Schema.instance.getCFMetaData(keyspace, columnFamily).getKeyValidator();
|
||||
|
||||
List<IMutation> rowMutations = new ArrayList<IMutation>(keys.size());
|
||||
|
|
|
|||
|
|
@ -17,55 +17,64 @@
|
|||
*/
|
||||
package org.apache.cassandra.cql;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.cassandra.config.*;
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.thrift.CfDef;
|
||||
import org.apache.cassandra.thrift.ColumnDef;
|
||||
|
||||
public class DropIndexStatement
|
||||
{
|
||||
public final String index;
|
||||
public final String indexName;
|
||||
private String keyspace;
|
||||
|
||||
public DropIndexStatement(String indexName)
|
||||
{
|
||||
index = indexName;
|
||||
this.indexName = indexName;
|
||||
}
|
||||
|
||||
public CFMetaData generateCFMetadataUpdate(String keyspace)
|
||||
throws InvalidRequestException, ConfigurationException, IOException
|
||||
public void setKeyspace(String keyspace)
|
||||
{
|
||||
CfDef cfDef = null;
|
||||
this.keyspace = keyspace;
|
||||
}
|
||||
|
||||
public String getColumnFamily() throws InvalidRequestException
|
||||
{
|
||||
return findIndexedCF().cfName;
|
||||
}
|
||||
|
||||
public CFMetaData generateCFMetadataUpdate() throws InvalidRequestException
|
||||
{
|
||||
return updateCFMetadata(findIndexedCF());
|
||||
}
|
||||
|
||||
private CFMetaData updateCFMetadata(CFMetaData cfm) throws InvalidRequestException
|
||||
{
|
||||
ColumnDefinition column = findIndexedColumn(cfm);
|
||||
assert column != null;
|
||||
CFMetaData cloned = cfm.clone();
|
||||
ColumnDefinition toChange = cloned.getColumn_metadata().get(column.name);
|
||||
assert toChange.getIndexName() != null && toChange.getIndexName().equals(indexName);
|
||||
toChange.setIndexName(null);
|
||||
toChange.setIndexType(null, null);
|
||||
return cloned;
|
||||
}
|
||||
|
||||
private CFMetaData findIndexedCF() throws InvalidRequestException
|
||||
{
|
||||
KSMetaData ksm = Schema.instance.getTableDefinition(keyspace);
|
||||
|
||||
for (CFMetaData cfm : ksm.cfMetaData().values())
|
||||
{
|
||||
cfDef = getUpdatedCFDef(cfm.toThrift());
|
||||
if (cfDef != null)
|
||||
break;
|
||||
if (findIndexedColumn(cfm) != null)
|
||||
return cfm;
|
||||
}
|
||||
|
||||
if (cfDef == null)
|
||||
throw new InvalidRequestException("Index '" + index + "' could not be found in any of the ColumnFamilies of keyspace '" + keyspace + "'");
|
||||
|
||||
return CFMetaData.fromThrift(cfDef);
|
||||
throw new InvalidRequestException("Index '" + indexName + "' could not be found in any of the column families of keyspace '" + keyspace + "'");
|
||||
}
|
||||
|
||||
private CfDef getUpdatedCFDef(CfDef cfDef) throws InvalidRequestException
|
||||
private ColumnDefinition findIndexedColumn(CFMetaData cfm)
|
||||
{
|
||||
for (ColumnDef column : cfDef.column_metadata)
|
||||
for (ColumnDefinition column : cfm.getColumn_metadata().values())
|
||||
{
|
||||
if (column.isSetIndex_type() && column.isSetIndex_name() && column.index_name.equals(index))
|
||||
{
|
||||
column.unsetIndex_name();
|
||||
column.unsetIndex_type();
|
||||
return cfDef;
|
||||
}
|
||||
if (column.getIndexType() != null && column.getIndexName() != null && column.getIndexName().equals(indexName))
|
||||
return column;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,11 @@ import java.nio.charset.CharacterCodingException;
|
|||
import java.util.*;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.apache.cassandra.auth.Permission;
|
||||
import org.apache.cassandra.config.*;
|
||||
import org.apache.cassandra.cli.CliUtils;
|
||||
|
|
@ -41,7 +46,6 @@ import org.apache.cassandra.service.StorageProxy;
|
|||
import org.apache.cassandra.service.StorageService;
|
||||
import org.apache.cassandra.service.MigrationManager;
|
||||
import org.apache.cassandra.thrift.Column;
|
||||
import org.apache.cassandra.db.ConsistencyLevel;
|
||||
import org.apache.cassandra.thrift.CqlMetadata;
|
||||
import org.apache.cassandra.thrift.CqlResult;
|
||||
import org.apache.cassandra.thrift.CqlResultType;
|
||||
|
|
@ -56,13 +60,8 @@ import org.apache.cassandra.utils.ByteBufferUtil;
|
|||
import org.apache.cassandra.utils.FBUtilities;
|
||||
import org.apache.cassandra.utils.Pair;
|
||||
import org.apache.cassandra.utils.SemanticVersion;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.antlr.runtime.*;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.apache.cassandra.thrift.ThriftValidation.validateColumnFamily;
|
||||
|
||||
|
|
@ -222,35 +221,6 @@ public class QueryProcessor
|
|||
return rows.subList(0, select.getNumRecords() < rows.size() ? select.getNumRecords() : rows.size());
|
||||
}
|
||||
|
||||
private static void batchUpdate(ThriftClientState clientState, List<UpdateStatement> updateStatements, ConsistencyLevel consistency, List<ByteBuffer> variables )
|
||||
throws RequestValidationException, RequestExecutionException
|
||||
{
|
||||
String globalKeyspace = clientState.getKeyspace();
|
||||
List<IMutation> rowMutations = new ArrayList<IMutation>(updateStatements.size());
|
||||
List<String> cfamsSeen = new ArrayList<String>(updateStatements.size());
|
||||
|
||||
for (UpdateStatement update : updateStatements)
|
||||
{
|
||||
String keyspace = update.keyspace == null ? globalKeyspace : update.keyspace;
|
||||
|
||||
// Avoid unnecessary authorizations.
|
||||
if (!(cfamsSeen.contains(update.getColumnFamily())))
|
||||
{
|
||||
clientState.hasColumnFamilyAccess(keyspace, update.getColumnFamily(), Permission.UPDATE);
|
||||
cfamsSeen.add(update.getColumnFamily());
|
||||
}
|
||||
|
||||
rowMutations.addAll(update.prepareRowMutations(keyspace, clientState, variables));
|
||||
}
|
||||
|
||||
for (IMutation mutation : rowMutations)
|
||||
{
|
||||
validateKey(mutation.key());
|
||||
}
|
||||
|
||||
StorageProxy.mutate(rowMutations, consistency);
|
||||
}
|
||||
|
||||
private static IDiskAtomFilter filterFromSelect(SelectStatement select, CFMetaData metadata, List<ByteBuffer> variables)
|
||||
throws InvalidRequestException
|
||||
{
|
||||
|
|
@ -556,8 +526,18 @@ 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);
|
||||
|
||||
keyspace = update.keyspace == null ? clientState.getKeyspace() : update.keyspace;
|
||||
// permission is checked in prepareRowMutations()
|
||||
List<IMutation> rowMutations = update.prepareRowMutations(keyspace, clientState, variables);
|
||||
|
||||
for (IMutation mutation : rowMutations)
|
||||
{
|
||||
validateKey(mutation.key());
|
||||
}
|
||||
|
||||
StorageProxy.mutate(rowMutations, update.getConsistencyLevel());
|
||||
|
||||
result.type = CqlResultType.VOID;
|
||||
return result;
|
||||
|
||||
|
|
@ -601,7 +581,7 @@ public class QueryProcessor
|
|||
keyspace = columnFamily.left == null ? clientState.getKeyspace() : columnFamily.left;
|
||||
|
||||
validateColumnFamily(keyspace, columnFamily.right);
|
||||
clientState.hasColumnFamilyAccess(keyspace, columnFamily.right, Permission.DELETE);
|
||||
clientState.hasColumnFamilyAccess(keyspace, columnFamily.right, Permission.MODIFY);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -623,7 +603,7 @@ public class QueryProcessor
|
|||
DeleteStatement delete = (DeleteStatement)statement.statement;
|
||||
|
||||
keyspace = delete.keyspace == null ? clientState.getKeyspace() : delete.keyspace;
|
||||
clientState.hasColumnFamilyAccess(keyspace, delete.columnFamily, Permission.DELETE);
|
||||
// permission is checked in prepareRowMutations()
|
||||
List<IMutation> deletions = delete.prepareRowMutations(keyspace, clientState, variables);
|
||||
for (IMutation deletion : deletions)
|
||||
{
|
||||
|
|
@ -639,7 +619,7 @@ public class QueryProcessor
|
|||
CreateKeyspaceStatement create = (CreateKeyspaceStatement)statement.statement;
|
||||
create.validate();
|
||||
ThriftValidation.validateKeyspaceNotSystem(create.getName());
|
||||
clientState.hasKeyspaceAccess(create.getName(), Permission.CREATE);
|
||||
clientState.hasAllKeyspacesAccess(Permission.CREATE);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -662,7 +642,7 @@ public class QueryProcessor
|
|||
|
||||
case CREATE_COLUMNFAMILY:
|
||||
CreateColumnFamilyStatement createCf = (CreateColumnFamilyStatement)statement.statement;
|
||||
clientState.hasColumnFamilySchemaAccess(keyspace, Permission.CREATE);
|
||||
clientState.hasKeyspaceAccess(keyspace, Permission.CREATE);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -723,12 +703,13 @@ public class QueryProcessor
|
|||
|
||||
case DROP_INDEX:
|
||||
DropIndexStatement dropIdx = (DropIndexStatement)statement.statement;
|
||||
keyspace = clientState.getKeyspace();
|
||||
dropIdx.setKeyspace(keyspace);
|
||||
clientState.hasColumnFamilyAccess(keyspace, dropIdx.getColumnFamily(), Permission.ALTER);
|
||||
|
||||
try
|
||||
{
|
||||
CFMetaData updatedCF = dropIdx.generateCFMetadataUpdate(clientState.getKeyspace());
|
||||
clientState.hasColumnFamilyAccess(updatedCF.ksName, updatedCF.cfName, Permission.DESCRIBE);
|
||||
|
||||
CFMetaData updatedCF = dropIdx.generateCFMetadataUpdate();
|
||||
MigrationManager.announceColumnFamilyUpdate(updatedCF);
|
||||
}
|
||||
catch (ConfigurationException e)
|
||||
|
|
@ -737,12 +718,6 @@ public class QueryProcessor
|
|||
ex.initCause(e);
|
||||
throw ex;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
InvalidRequestException ex = new InvalidRequestException(e.toString());
|
||||
ex.initCause(e);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
result.type = CqlResultType.VOID;
|
||||
return result;
|
||||
|
|
@ -788,7 +763,7 @@ public class QueryProcessor
|
|||
AlterTableStatement alterTable = (AlterTableStatement) statement.statement;
|
||||
|
||||
validateColumnFamily(keyspace, alterTable.columnFamily);
|
||||
clientState.hasColumnFamilyAccess(alterTable.columnFamily, Permission.ALTER);
|
||||
clientState.hasColumnFamilyAccess(keyspace, alterTable.columnFamily, Permission.ALTER);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ import org.apache.cassandra.thrift.ThriftClientState;
|
|||
|
||||
import static org.apache.cassandra.cql.QueryProcessor.validateColumn;
|
||||
import static org.apache.cassandra.cql.QueryProcessor.validateKey;
|
||||
|
||||
import static org.apache.cassandra.thrift.ThriftValidation.validateColumnFamily;
|
||||
|
||||
/**
|
||||
|
|
@ -132,8 +131,6 @@ public class UpdateStatement extends AbstractModification
|
|||
public List<IMutation> prepareRowMutations(String keyspace, ThriftClientState clientState, Long timestamp, List<ByteBuffer> variables)
|
||||
throws InvalidRequestException, UnauthorizedException
|
||||
{
|
||||
List<String> cfamsSeen = new ArrayList<String>();
|
||||
|
||||
boolean hasCommutativeOperation = false;
|
||||
|
||||
for (Map.Entry<Term, Operation> column : getColumns().entrySet())
|
||||
|
|
@ -151,12 +148,7 @@ public class UpdateStatement extends AbstractModification
|
|||
|
||||
QueryProcessor.validateKeyAlias(metadata, keyName);
|
||||
|
||||
// Avoid unnecessary authorizations.
|
||||
if (!(cfamsSeen.contains(columnFamily)))
|
||||
{
|
||||
clientState.hasColumnFamilyAccess(columnFamily, Permission.UPDATE);
|
||||
cfamsSeen.add(columnFamily);
|
||||
}
|
||||
clientState.hasColumnFamilyAccess(keyspace, columnFamily, Permission.MODIFY);
|
||||
|
||||
List<IMutation> rowMutations = new LinkedList<IMutation>();
|
||||
|
||||
|
|
|
|||
|
|
@ -29,13 +29,17 @@ options {
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.cassandra.cql3.operations.*;
|
||||
import org.apache.cassandra.auth.Permission;
|
||||
import org.apache.cassandra.auth.DataResource;
|
||||
import org.apache.cassandra.auth.IResource;
|
||||
import org.apache.cassandra.cql3.operations.*;
|
||||
import org.apache.cassandra.cql3.statements.*;
|
||||
import org.apache.cassandra.db.marshal.CollectionType;
|
||||
import org.apache.cassandra.exceptions.ConfigurationException;
|
||||
|
|
@ -167,7 +171,7 @@ cqlStatement returns [ParsedStatement stmt]
|
|||
| st14=alterTableStatement { $stmt = st14; }
|
||||
| st15=grantStatement { $stmt = st15; }
|
||||
| st16=revokeStatement { $stmt = st16; }
|
||||
| st17=listGrantsStatement { $stmt = st17; }
|
||||
| st17=listPermissionsStatement { $stmt = st17; }
|
||||
| st18=alterKeyspaceStatement { $stmt = st18; }
|
||||
;
|
||||
|
||||
|
|
@ -181,7 +185,6 @@ useStatement returns [UseStatement stmt]
|
|||
/**
|
||||
* SELECT <expression>
|
||||
* FROM <CF>
|
||||
* USING CONSISTENCY <LEVEL>
|
||||
* WHERE KEY = "key1" AND COL > 1 AND COL < 100
|
||||
* LIMIT <NUMBER>;
|
||||
*/
|
||||
|
|
@ -277,7 +280,7 @@ usingClauseObjective[Attributes attrs]
|
|||
|
||||
/**
|
||||
* UPDATE <CF>
|
||||
* USING CONSISTENCY <level> AND TIMESTAMP <long>
|
||||
* USING TIMESTAMP <long>
|
||||
* SET name1 = value1, name2 = value2
|
||||
* WHERE key = value;
|
||||
*/
|
||||
|
|
@ -298,7 +301,7 @@ updateStatement returns [UpdateStatement expr]
|
|||
/**
|
||||
* DELETE name1, name2
|
||||
* FROM <CF>
|
||||
* USING CONSISTENCY <level> AND TIMESTAMP <long>
|
||||
* USING TIMESTAMP <long>
|
||||
* WHERE KEY = keyname;
|
||||
*/
|
||||
deleteStatement returns [DeleteStatement expr]
|
||||
|
|
@ -325,7 +328,7 @@ deleteSelector returns [Selector s]
|
|||
;
|
||||
|
||||
/**
|
||||
* BEGIN BATCH [USING CONSISTENCY <LVL>]
|
||||
* BEGIN BATCH
|
||||
* UPDATE <CF> SET name1 = value1 WHERE KEY = keyname1;
|
||||
* UPDATE <CF> SET name2 = value2 WHERE KEY = keyname2;
|
||||
* UPDATE <CF> SET name3 = value3 WHERE KEY = keyname3;
|
||||
|
|
@ -334,7 +337,7 @@ deleteSelector returns [Selector s]
|
|||
*
|
||||
* OR
|
||||
*
|
||||
* BEGIN BATCH [USING CONSISTENCY <LVL>]
|
||||
* BEGIN BATCH
|
||||
* INSERT INTO <CF> (KEY, <name>) VALUES ('<key>', '<value>');
|
||||
* INSERT INTO <CF> (KEY, <name>) VALUES ('<key>', '<value>');
|
||||
* ...
|
||||
|
|
@ -342,7 +345,7 @@ deleteSelector returns [Selector s]
|
|||
*
|
||||
* OR
|
||||
*
|
||||
* BEGIN BATCH [USING CONSISTENCY <LVL>]
|
||||
* BEGIN BATCH
|
||||
* DELETE name1, name2 FROM <CF> WHERE key = <key>
|
||||
* DELETE name3, name4 FROM <CF> WHERE key = <key>
|
||||
* ...
|
||||
|
|
@ -493,23 +496,16 @@ truncateStatement returns [TruncateStatement stmt]
|
|||
;
|
||||
|
||||
/**
|
||||
* GRANT <permission> ON <resource> TO <username> [WITH GRANT OPTION]
|
||||
* GRANT <permission> ON <resource> TO <username>
|
||||
*/
|
||||
grantStatement returns [GrantStatement stmt]
|
||||
@init { boolean withGrant = false; }
|
||||
: K_GRANT
|
||||
permission
|
||||
permissionOrAll
|
||||
K_ON
|
||||
resource=columnFamilyName
|
||||
resource
|
||||
K_TO
|
||||
user=(IDENT | STRING_LITERAL)
|
||||
(K_WITH K_GRANT K_OPTION { withGrant = true; })?
|
||||
{
|
||||
$stmt = new GrantStatement($permission.perm,
|
||||
resource,
|
||||
$user.text,
|
||||
withGrant);
|
||||
}
|
||||
username
|
||||
{ $stmt = new GrantStatement($permissionOrAll.perms, $resource.res, $username.text); }
|
||||
;
|
||||
|
||||
/**
|
||||
|
|
@ -517,26 +513,54 @@ grantStatement returns [GrantStatement stmt]
|
|||
*/
|
||||
revokeStatement returns [RevokeStatement stmt]
|
||||
: K_REVOKE
|
||||
permission
|
||||
permissionOrAll
|
||||
K_ON
|
||||
resource=columnFamilyName
|
||||
resource
|
||||
K_FROM
|
||||
user=(IDENT | STRING_LITERAL)
|
||||
{
|
||||
$stmt = new RevokeStatement($permission.perm,
|
||||
$user.text,
|
||||
resource);
|
||||
}
|
||||
username
|
||||
{ $stmt = new RevokeStatement($permissionOrAll.perms, $resource.res, $username.text); }
|
||||
;
|
||||
|
||||
listGrantsStatement returns [ListGrantsStatement stmt]
|
||||
: K_LIST K_GRANTS K_FOR username=(IDENT | STRING_LITERAL) { $stmt = new ListGrantsStatement($username.text); }
|
||||
listPermissionsStatement returns [ListPermissionsStatement stmt]
|
||||
@init {
|
||||
IResource resource = null;
|
||||
String username = null;
|
||||
boolean recursive = true;
|
||||
}
|
||||
: K_LIST
|
||||
permissionOrAll
|
||||
( K_ON resource { resource = $resource.res; } )?
|
||||
( K_OF username { username = $username.text; } )?
|
||||
( K_NORECURSIVE { recursive = false; } )?
|
||||
{ $stmt = new ListPermissionsStatement($permissionOrAll.perms, resource, username, recursive); }
|
||||
;
|
||||
|
||||
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)
|
||||
: p=(K_CREATE | K_ALTER | K_DROP | K_SELECT | K_MODIFY | K_AUTHORIZE)
|
||||
{ $perm = Permission.valueOf($p.text.toUpperCase()); }
|
||||
;
|
||||
|
||||
permissionOrAll returns [Set<Permission> perms]
|
||||
: K_ALL ( K_PERMISSIONS )? { $perms = Permission.ALL_DATA; }
|
||||
| p=permission ( K_PERMISSION )? { $perms = EnumSet.of($p.perm); }
|
||||
;
|
||||
|
||||
username
|
||||
: IDENT
|
||||
| STRING_LITERAL
|
||||
;
|
||||
|
||||
resource returns [IResource res]
|
||||
: r=dataResource { $res = $r.res; }
|
||||
;
|
||||
|
||||
dataResource returns [DataResource res]
|
||||
: K_ALL K_KEYSPACES { $res = DataResource.root(); }
|
||||
| K_KEYSPACE ks = keyspaceName { $res = DataResource.keyspace($ks.id); }
|
||||
| ( K_COLUMNFAMILY )? cf = columnFamilyName
|
||||
{ $res = DataResource.columnFamily($cf.name.getKeyspace(), $cf.name.getColumnFamily()); }
|
||||
;
|
||||
|
||||
/** DEFINITIONS **/
|
||||
|
||||
// Column Identifiers
|
||||
|
|
@ -747,7 +771,6 @@ collection_type returns [ParsedType pt]
|
|||
unreserved_keyword returns [String str]
|
||||
: k=( K_KEY
|
||||
| K_CLUSTERING
|
||||
| K_LEVEL
|
||||
| K_COUNT
|
||||
| K_TTL
|
||||
| K_COMPACT
|
||||
|
|
@ -758,6 +781,10 @@ unreserved_keyword returns [String str]
|
|||
| K_MAP
|
||||
| K_LIST
|
||||
| K_FILTERING
|
||||
| K_PERMISSION
|
||||
| K_PERMISSIONS
|
||||
| K_KEYSPACES
|
||||
| K_ALL
|
||||
) { $str = $k.text; }
|
||||
| t=native_type { $str = t.toString(); }
|
||||
;
|
||||
|
|
@ -774,16 +801,6 @@ 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_LEVEL: ( O N E
|
||||
| Q U O R U M
|
||||
| A L L
|
||||
| A N Y
|
||||
| L O C A L '_' Q U O R U M
|
||||
| E A C H '_' Q U O R U M
|
||||
| T W O
|
||||
| T H R E E
|
||||
)
|
||||
;
|
||||
K_USE: U S E;
|
||||
K_COUNT: C O U N T;
|
||||
K_SET: S E T;
|
||||
|
|
@ -797,6 +814,7 @@ K_IN: I N;
|
|||
K_CREATE: C R E A T E;
|
||||
K_KEYSPACE: ( K E Y S P A C E
|
||||
| S C H E M A );
|
||||
K_KEYSPACES: K E Y S P A C E S;
|
||||
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;
|
||||
|
|
@ -819,16 +837,16 @@ K_BY: B Y;
|
|||
K_ASC: A S C;
|
||||
K_DESC: D E S C;
|
||||
K_GRANT: G R A N T;
|
||||
K_GRANTS: G R A N T S;
|
||||
K_ALL: A L L;
|
||||
K_PERMISSION: P E R M I S S I O N;
|
||||
K_PERMISSIONS: P E R M I S S I O N S;
|
||||
K_OF: O F;
|
||||
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_ALLOW: A L L O W;
|
||||
K_FILTERING: F I L T E R I N G;
|
||||
|
||||
K_MODIFY: M O D I F Y;
|
||||
K_AUTHORIZE: A U T H O R I Z E;
|
||||
K_NORECURSIVE: N O R E C U R S I V E;
|
||||
|
||||
K_CLUSTERING: C L U S T E R I N G;
|
||||
K_ASCII: A S C I I;
|
||||
|
|
|
|||
|
|
@ -127,8 +127,8 @@ public class QueryProcessor
|
|||
throws RequestExecutionException, RequestValidationException
|
||||
{
|
||||
ClientState clientState = queryState.getClientState();
|
||||
statement.checkAccess(clientState);
|
||||
statement.validate(clientState);
|
||||
statement.checkAccess(clientState);
|
||||
ResultMessage result = statement.execute(cl, queryState, variables);
|
||||
return result == null ? new ResultMessage.Void() : result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.DataResource;
|
||||
import org.apache.cassandra.cql3.CQLStatement;
|
||||
import org.apache.cassandra.db.ConsistencyLevel;
|
||||
import org.apache.cassandra.exceptions.*;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
import org.apache.cassandra.service.QueryState;
|
||||
import org.apache.cassandra.transport.messages.ResultMessage;
|
||||
|
||||
public abstract class AuthorizationStatement extends ParsedStatement implements CQLStatement
|
||||
{
|
||||
@Override
|
||||
public Prepared prepare()
|
||||
{
|
||||
return new Prepared(this);
|
||||
}
|
||||
|
||||
public int getBoundsTerms()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void checkAccess(ClientState state) throws InvalidRequestException, UnauthorizedException
|
||||
{}
|
||||
|
||||
public ResultMessage execute(ConsistencyLevel cl, QueryState state, List<ByteBuffer> variables) throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
return execute(state.getClientState(), variables);
|
||||
}
|
||||
|
||||
public abstract ResultMessage execute(ClientState state, List<ByteBuffer> variables) throws UnauthorizedException, InvalidRequestException;
|
||||
|
||||
public ResultMessage executeInternal(QueryState state)
|
||||
{
|
||||
// executeInternal is for local query only, thus altering permission doesn't make sense and is not supported
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public static DataResource maybeCorrectResource(DataResource resource, ClientState state) throws InvalidRequestException
|
||||
{
|
||||
if (resource.isColumnFamilyLevel() && resource.getKeyspace() == null)
|
||||
return DataResource.columnFamily(state.getKeyspace(), resource.getColumnFamily());
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
|
|
@ -70,7 +70,7 @@ public class BatchStatement extends ModificationStatement
|
|||
// Avoid unnecessary authorizations.
|
||||
if (!(cfamsSeen.contains(statement.columnFamily())))
|
||||
{
|
||||
state.hasColumnFamilyAccess(statement.keyspace(), statement.columnFamily(), Permission.UPDATE);
|
||||
state.hasColumnFamilyAccess(statement.keyspace(), statement.columnFamily(), Permission.MODIFY);
|
||||
cfamsSeen.add(statement.columnFamily());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ public class CreateColumnFamilyStatement extends SchemaAlteringStatement
|
|||
|
||||
public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.CREATE);
|
||||
state.hasKeyspaceAccess(keyspace(), Permission.CREATE);
|
||||
}
|
||||
|
||||
// Column definitions
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ public class CreateKeyspaceStatement extends SchemaAlteringStatement
|
|||
|
||||
public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
state.hasKeyspaceAccess(name, Permission.CREATE);
|
||||
state.hasAllKeyspacesAccess(Permission.CREATE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
*/
|
||||
package org.apache.cassandra.cql3.statements;
|
||||
|
||||
|
||||
import org.apache.cassandra.auth.Permission;
|
||||
import org.apache.cassandra.config.CFMetaData;
|
||||
import org.apache.cassandra.config.ColumnDefinition;
|
||||
|
|
@ -41,44 +40,7 @@ public class DropIndexStatement extends SchemaAlteringStatement
|
|||
|
||||
public void checkAccess(ClientState state) throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.ALTER);
|
||||
}
|
||||
|
||||
public void announceMigration() throws InvalidRequestException, ConfigurationException
|
||||
{
|
||||
CFMetaData updatedCfm = null;
|
||||
|
||||
KSMetaData ksm = Schema.instance.getTableDefinition(keyspace());
|
||||
|
||||
for (CFMetaData cfm : ksm.cfMetaData().values())
|
||||
{
|
||||
updatedCfm = getUpdatedCFMetadata(cfm);
|
||||
if (updatedCfm != null)
|
||||
break;
|
||||
}
|
||||
|
||||
if (updatedCfm == null)
|
||||
throw new InvalidRequestException("Index '" + indexName + "' could not be found in any of the column families of keyspace '" + keyspace() + "'");
|
||||
|
||||
MigrationManager.announceColumnFamilyUpdate(updatedCfm);
|
||||
}
|
||||
|
||||
private CFMetaData getUpdatedCFMetadata(CFMetaData cfm) throws InvalidRequestException
|
||||
{
|
||||
for (ColumnDefinition column : cfm.getColumn_metadata().values())
|
||||
{
|
||||
if (column.getIndexType() != null && column.getIndexName() != null && column.getIndexName().equals(indexName))
|
||||
{
|
||||
CFMetaData cloned = cfm.clone();
|
||||
ColumnDefinition toChange = cloned.getColumn_metadata().get(column.name);
|
||||
assert toChange.getIndexName() != null && toChange.getIndexName().equals(indexName);
|
||||
toChange.setIndexName(null);
|
||||
toChange.setIndexType(null, null);
|
||||
return cloned;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
state.hasColumnFamilyAccess(keyspace(), findIndexedCF().cfName, Permission.ALTER);
|
||||
}
|
||||
|
||||
public ResultMessage.SchemaChange.Change changeType()
|
||||
|
|
@ -86,4 +48,43 @@ public class DropIndexStatement extends SchemaAlteringStatement
|
|||
// Dropping an index is akin to updating the CF
|
||||
return ResultMessage.SchemaChange.Change.UPDATED;
|
||||
}
|
||||
|
||||
public void announceMigration() throws InvalidRequestException, ConfigurationException
|
||||
{
|
||||
CFMetaData updatedCfm = updateCFMetadata(findIndexedCF());
|
||||
MigrationManager.announceColumnFamilyUpdate(updatedCfm);
|
||||
}
|
||||
|
||||
private CFMetaData updateCFMetadata(CFMetaData cfm) throws InvalidRequestException
|
||||
{
|
||||
ColumnDefinition column = findIndexedColumn(cfm);
|
||||
assert column != null;
|
||||
CFMetaData cloned = cfm.clone();
|
||||
ColumnDefinition toChange = cloned.getColumn_metadata().get(column.name);
|
||||
assert toChange.getIndexName() != null && toChange.getIndexName().equals(indexName);
|
||||
toChange.setIndexName(null);
|
||||
toChange.setIndexType(null, null);
|
||||
return cloned;
|
||||
}
|
||||
|
||||
private CFMetaData findIndexedCF() throws InvalidRequestException
|
||||
{
|
||||
KSMetaData ksm = Schema.instance.getTableDefinition(keyspace());
|
||||
for (CFMetaData cfm : ksm.cfMetaData().values())
|
||||
{
|
||||
if (findIndexedColumn(cfm) != null)
|
||||
return cfm;
|
||||
}
|
||||
throw new InvalidRequestException("Index '" + indexName + "' could not be found in any of the column families of keyspace '" + keyspace() + "'");
|
||||
}
|
||||
|
||||
private ColumnDefinition findIndexedColumn(CFMetaData cfm)
|
||||
{
|
||||
for (ColumnDefinition column : cfm.getColumn_metadata().values())
|
||||
{
|
||||
if (column.getIndexType() != null && column.getIndexName() != null && column.getIndexName().equals(indexName))
|
||||
return column;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,10 @@ package org.apache.cassandra.cql3.statements;
|
|||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.cassandra.auth.IResource;
|
||||
import org.apache.cassandra.auth.Permission;
|
||||
import org.apache.cassandra.cql3.CFName;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.exceptions.UnauthorizedException;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
|
|
@ -30,22 +31,14 @@ import org.apache.cassandra.transport.messages.ResultMessage;
|
|||
|
||||
public class GrantStatement extends PermissionAlteringStatement
|
||||
{
|
||||
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)
|
||||
public GrantStatement(Set<Permission> permissions, IResource resource, String username)
|
||||
{
|
||||
this.permission = permission;
|
||||
this.resource = resource;
|
||||
this.username = username;
|
||||
this.grantOption = grantOption;
|
||||
super(permissions, resource, username);
|
||||
}
|
||||
|
||||
public ResultMessage execute(ClientState state, List<ByteBuffer> variables) throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
state.grantPermission(permission, username, resource, grantOption);
|
||||
state.grantPermission(permissions, resource, username);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.cassandra.cql3.statements;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
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 PermissionAlteringStatement
|
||||
{
|
||||
private final String username;
|
||||
|
||||
public ListGrantsStatement(String username)
|
||||
{
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public ResultMessage execute(ClientState state, List<ByteBuffer> variables) throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
return state.listPermissions(username);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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.*;
|
||||
|
||||
import org.apache.cassandra.auth.*;
|
||||
import org.apache.cassandra.cql3.ColumnIdentifier;
|
||||
import org.apache.cassandra.cql3.ColumnSpecification;
|
||||
import org.apache.cassandra.cql3.ResultSet;
|
||||
import org.apache.cassandra.db.marshal.UTF8Type;
|
||||
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 ListPermissionsStatement extends AuthorizationStatement
|
||||
{
|
||||
private static final String KS = "auth"; // virtual keyspace to use for now.
|
||||
private static final String CF = "permissions"; // virtual cf to use for now.
|
||||
|
||||
private static final List<ColumnSpecification> metadata;
|
||||
|
||||
static
|
||||
{
|
||||
List<ColumnSpecification> columns = new ArrayList<ColumnSpecification>(4);
|
||||
columns.add(new ColumnSpecification(KS, CF, new ColumnIdentifier("username", true), UTF8Type.instance));
|
||||
columns.add(new ColumnSpecification(KS, CF, new ColumnIdentifier("resource", true), UTF8Type.instance));
|
||||
columns.add(new ColumnSpecification(KS, CF, new ColumnIdentifier("permission", true), UTF8Type.instance));
|
||||
metadata = Collections.unmodifiableList(columns);
|
||||
}
|
||||
|
||||
private final Set<Permission> permissions;
|
||||
private DataResource resource;
|
||||
private final String username;
|
||||
private final boolean recursive;
|
||||
|
||||
public ListPermissionsStatement(Set<Permission> permissions, IResource resource, String username, boolean recursive)
|
||||
{
|
||||
this.permissions = permissions;
|
||||
this.resource = (DataResource) resource;
|
||||
this.username = username;
|
||||
this.recursive = recursive;
|
||||
}
|
||||
|
||||
// TODO: user existence check (when IAuthenticator rewrite is done)
|
||||
public void validate(ClientState state) throws InvalidRequestException
|
||||
{
|
||||
if (resource != null)
|
||||
{
|
||||
resource = maybeCorrectResource(resource, state);
|
||||
if (!resource.exists())
|
||||
throw new InvalidRequestException(String.format("%s doesn't exist", resource));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Create a new ResultMessage type (?). Rows will do for now.
|
||||
public ResultMessage execute(ClientState state, List<ByteBuffer> variables) throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
List<PermissionDetails> details = new ArrayList<PermissionDetails>();
|
||||
|
||||
if (resource != null && recursive)
|
||||
{
|
||||
for (IResource r : Resources.chain(resource))
|
||||
details.addAll(state.listPermissions(permissions, r, username));
|
||||
}
|
||||
else
|
||||
{
|
||||
details.addAll(state.listPermissions(permissions, resource, username));
|
||||
}
|
||||
|
||||
Collections.sort(details);
|
||||
return resultMessage(details);
|
||||
}
|
||||
|
||||
private ResultMessage resultMessage(List<PermissionDetails> details)
|
||||
{
|
||||
if (details.isEmpty())
|
||||
return new ResultMessage.Void();
|
||||
|
||||
ResultSet result = new ResultSet(metadata);
|
||||
for (PermissionDetails pd : details)
|
||||
{
|
||||
result.addColumnValue(UTF8Type.instance.decompose(pd.username));
|
||||
result.addColumnValue(UTF8Type.instance.decompose(pd.resource.toString()));
|
||||
result.addColumnValue(UTF8Type.instance.decompose(pd.permission.toString()));
|
||||
}
|
||||
return new ResultMessage.Rows(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -24,8 +24,6 @@ import java.util.*;
|
|||
|
||||
import org.apache.cassandra.auth.Permission;
|
||||
import org.apache.cassandra.cql3.*;
|
||||
|
||||
import org.apache.cassandra.transport.messages.ResultMessage;
|
||||
import org.apache.cassandra.db.*;
|
||||
import org.apache.cassandra.db.filter.ColumnSlice;
|
||||
import org.apache.cassandra.db.filter.QueryPath;
|
||||
|
|
@ -37,6 +35,7 @@ import org.apache.cassandra.db.ExpiringColumn;
|
|||
import org.apache.cassandra.service.ClientState;
|
||||
import org.apache.cassandra.service.QueryState;
|
||||
import org.apache.cassandra.service.StorageProxy;
|
||||
import org.apache.cassandra.transport.messages.ResultMessage;
|
||||
|
||||
/**
|
||||
* Abstract class for statements that apply on a given column family.
|
||||
|
|
@ -69,7 +68,7 @@ public abstract class ModificationStatement extends CFStatement implements CQLSt
|
|||
|
||||
public void checkAccess(ClientState state) throws InvalidRequestException, UnauthorizedException
|
||||
{
|
||||
state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.UPDATE);
|
||||
state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.MODIFY);
|
||||
}
|
||||
|
||||
public void validate(ClientState state) throws InvalidRequestException
|
||||
|
|
|
|||
|
|
@ -17,45 +17,43 @@
|
|||
*/
|
||||
package org.apache.cassandra.cql3.statements;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.cassandra.cql3.CQLStatement;
|
||||
import org.apache.cassandra.db.ConsistencyLevel;
|
||||
import org.apache.cassandra.exceptions.*;
|
||||
import org.apache.cassandra.auth.DataResource;
|
||||
import org.apache.cassandra.auth.IResource;
|
||||
import org.apache.cassandra.auth.Permission;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.exceptions.UnauthorizedException;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
import org.apache.cassandra.service.QueryState;
|
||||
import org.apache.cassandra.transport.messages.ResultMessage;
|
||||
|
||||
public abstract class PermissionAlteringStatement extends ParsedStatement implements CQLStatement
|
||||
public abstract class PermissionAlteringStatement extends AuthorizationStatement
|
||||
{
|
||||
@Override
|
||||
public Prepared prepare()
|
||||
protected final Set<Permission> permissions;
|
||||
protected DataResource resource;
|
||||
protected final String username;
|
||||
|
||||
protected PermissionAlteringStatement(Set<Permission> permissions, IResource resource, String username)
|
||||
{
|
||||
return new Prepared(this);
|
||||
this.permissions = permissions;
|
||||
this.resource = (DataResource) resource;
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public int getBoundsTerms()
|
||||
public void checkAccess(ClientState state) throws InvalidRequestException, UnauthorizedException
|
||||
{
|
||||
return 0;
|
||||
// check that the user has AUTHORIZE permission on the resource or its parents, otherwise reject GRANT/REVOKE.
|
||||
state.ensureHasPermission(Permission.AUTHORIZE, resource);
|
||||
// check that the user has [a single permission or all in case of ALL] on the resource or its parents.
|
||||
for (Permission p : permissions)
|
||||
state.ensureHasPermission(p, resource);
|
||||
}
|
||||
|
||||
public void checkAccess(ClientState state)
|
||||
{}
|
||||
|
||||
public void validate(ClientState state)
|
||||
{}
|
||||
|
||||
public ResultMessage execute(ConsistencyLevel cl, QueryState state, List<ByteBuffer> variables) throws UnauthorizedException, InvalidRequestException
|
||||
// TODO: user existence check (when IAuthenticator rewrite is done)
|
||||
public void validate(ClientState state) throws InvalidRequestException
|
||||
{
|
||||
return execute(state.getClientState(), variables);
|
||||
}
|
||||
|
||||
public abstract ResultMessage execute(ClientState state, List<ByteBuffer> variables) throws UnauthorizedException, InvalidRequestException;
|
||||
|
||||
public ResultMessage executeInternal(QueryState state)
|
||||
{
|
||||
// executeInternal is for local query only, thus altering permission doesn't make sense and is not supported
|
||||
throw new UnsupportedOperationException();
|
||||
// if a keyspace is omitted when GRANT/REVOKE ON TABLE <table>, we need to correct the resource.
|
||||
resource = maybeCorrectResource(resource, state);
|
||||
if (!resource.exists())
|
||||
throw new InvalidRequestException(String.format("%s doesn't exist", resource));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,9 +20,10 @@ package org.apache.cassandra.cql3.statements;
|
|||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.cassandra.auth.IResource;
|
||||
import org.apache.cassandra.auth.Permission;
|
||||
import org.apache.cassandra.cql3.CFName;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.exceptions.UnauthorizedException;
|
||||
import org.apache.cassandra.service.ClientState;
|
||||
|
|
@ -30,20 +31,14 @@ import org.apache.cassandra.transport.messages.ResultMessage;
|
|||
|
||||
public class RevokeStatement extends PermissionAlteringStatement
|
||||
{
|
||||
private final Permission permission;
|
||||
private final String from;
|
||||
private final CFName resource;
|
||||
|
||||
public RevokeStatement(Permission permission, String from, CFName resource)
|
||||
public RevokeStatement(Set<Permission> permissions, IResource resource, String username)
|
||||
{
|
||||
this.permission = permission;
|
||||
this.from = from;
|
||||
this.resource = resource;
|
||||
super(permissions, resource, username);
|
||||
}
|
||||
|
||||
public ResultMessage execute(ClientState state, List<ByteBuffer> variables) throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
state.revokePermission(permission, from, resource);
|
||||
state.revokePermission(permissions, resource, username);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ public class TruncateStatement extends CFStatement implements CQLStatement
|
|||
|
||||
public void checkAccess(ClientState state) throws InvalidRequestException, UnauthorizedException
|
||||
{
|
||||
state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.DELETE);
|
||||
state.hasColumnFamilyAccess(keyspace(), columnFamily(), Permission.MODIFY);
|
||||
}
|
||||
|
||||
public void validate(ClientState state) throws InvalidRequestException
|
||||
|
|
|
|||
|
|
@ -219,6 +219,12 @@ public class CassandraDaemon
|
|||
System.exit(100);
|
||||
}
|
||||
|
||||
// TODO: setup authenticator
|
||||
// setup Authorizer.
|
||||
DatabaseDescriptor.getAuthorizer().setup();
|
||||
// register a custom MigrationListener for permissions cleanup after dropped keyspaces/cfs.
|
||||
MigrationManager.instance.register(new org.apache.cassandra.auth.MigrationListener());
|
||||
|
||||
// clean up debris in the rest of the tables
|
||||
for (String table : Schema.instance.getTables())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -19,15 +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.transport.messages.ResultMessage;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.apache.cassandra.auth.*;
|
||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||
import org.apache.cassandra.config.Schema;
|
||||
import org.apache.cassandra.db.SystemTable;
|
||||
import org.apache.cassandra.db.Table;
|
||||
import org.apache.cassandra.exceptions.InvalidRequestException;
|
||||
import org.apache.cassandra.exceptions.UnauthorizedException;
|
||||
|
|
@ -44,12 +43,28 @@ public class ClientState
|
|||
private static final Logger logger = LoggerFactory.getLogger(ClientState.class);
|
||||
public static final SemanticVersion DEFAULT_CQL_VERSION = org.apache.cassandra.cql3.QueryProcessor.CQL_VERSION;
|
||||
|
||||
private static final Set<IResource> READABLE_SYSTEM_RESOURCES = new HashSet<IResource>(5);
|
||||
private static final Set<IResource> PROTECTED_AUTH_RESOURCES = new HashSet<IResource>();
|
||||
|
||||
static
|
||||
{
|
||||
// We want these system cfs to be always readable since many tools rely on them (nodetool, cqlsh, bulkloader, etc.)
|
||||
String[] cfs = new String[] { SystemTable.LOCAL_CF,
|
||||
SystemTable.PEERS_CF,
|
||||
SystemTable.SCHEMA_KEYSPACES_CF,
|
||||
SystemTable.SCHEMA_COLUMNFAMILIES_CF,
|
||||
SystemTable.SCHEMA_COLUMNS_CF };
|
||||
for (String cf : cfs)
|
||||
READABLE_SYSTEM_RESOURCES.add(DataResource.columnFamily(Table.SYSTEM_KS, cf));
|
||||
|
||||
PROTECTED_AUTH_RESOURCES.addAll(DatabaseDescriptor.getAuthorizer().protectedResources());
|
||||
// TODO: the same with IAuthenticator once it's done.
|
||||
}
|
||||
|
||||
// Current user for the session
|
||||
private AuthenticatedUser user;
|
||||
private String keyspace;
|
||||
|
||||
// Reusable array for authorization
|
||||
private final List<Object> resource = new ArrayList<Object>();
|
||||
private SemanticVersion cqlVersion = DEFAULT_CQL_VERSION;
|
||||
|
||||
// internalCall is used to mark ClientState as used by some internal component
|
||||
|
|
@ -97,71 +112,57 @@ public class ClientState
|
|||
this.user = DatabaseDescriptor.getAuthenticator().authenticate(credentials);
|
||||
}
|
||||
|
||||
private void resourceClear()
|
||||
public void hasAllKeyspacesAccess(Permission perm) throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
resource.clear();
|
||||
resource.add(Resources.ROOT);
|
||||
resource.add(Resources.KEYSPACES);
|
||||
if (internalCall)
|
||||
return;
|
||||
validateLogin();
|
||||
ensureHasPermission(perm, DataResource.root());
|
||||
}
|
||||
|
||||
public void hasKeyspaceAccess(String keyspace, Permission perm) throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
hasColumnFamilySchemaAccess(keyspace, perm);
|
||||
hasAccess(keyspace, perm, DataResource.keyspace(keyspace));
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that the client thread has the given Permission for the ColumnFamily list of
|
||||
* the provided keyspace.
|
||||
*/
|
||||
public void hasColumnFamilySchemaAccess(String keyspace, Permission perm) throws UnauthorizedException, InvalidRequestException
|
||||
public void hasColumnFamilyAccess(String keyspace, String columnFamily, Permission perm)
|
||||
throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
hasAccess(keyspace, perm, DataResource.columnFamily(keyspace, columnFamily));
|
||||
}
|
||||
|
||||
private void hasAccess(String keyspace, Permission perm, DataResource resource)
|
||||
throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
validateLogin();
|
||||
validateKeyspace(keyspace);
|
||||
|
||||
preventSystemKSSchemaModification(keyspace, perm);
|
||||
|
||||
resourceClear();
|
||||
resource.add(keyspace);
|
||||
Set<Permission> perms = DatabaseDescriptor.getAuthority().authorize(user, resource);
|
||||
|
||||
hasAccess(user, perms, perm, resource);
|
||||
}
|
||||
|
||||
private void preventSystemKSSchemaModification(String keyspace, Permission perm) throws InvalidRequestException
|
||||
{
|
||||
if (keyspace.equalsIgnoreCase(Table.SYSTEM_KS) && !Permission.ALLOWED_SYSTEM_ACTIONS.contains(perm))
|
||||
throw new InvalidRequestException("system keyspace is not user-modifiable.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that the client thread has the given Permission in the context of the given
|
||||
* ColumnFamily and the current keyspace.
|
||||
*/
|
||||
public void hasColumnFamilyAccess(String columnFamily, Permission perm) throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
hasColumnFamilyAccess(keyspace, columnFamily, perm);
|
||||
}
|
||||
|
||||
public void hasColumnFamilyAccess(String keyspace, String columnFamily, Permission perm) throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
validateLogin();
|
||||
validateKeyspace(keyspace);
|
||||
|
||||
resourceClear();
|
||||
resource.add(keyspace);
|
||||
|
||||
if (!internalCall)
|
||||
preventSystemKSSchemaModification(keyspace, perm);
|
||||
|
||||
// check if keyspace access is set to Permission.FULL_ACCESS
|
||||
// (which means that user has all access on keyspace and it's underlying elements)
|
||||
if (DatabaseDescriptor.getAuthority().authorize(user, resource).contains(Permission.FULL_ACCESS))
|
||||
if (internalCall)
|
||||
return;
|
||||
validateLogin();
|
||||
preventSystemKSSModification(keyspace, perm);
|
||||
if (perm.equals(Permission.SELECT) && READABLE_SYSTEM_RESOURCES.contains(resource))
|
||||
return;
|
||||
if (PROTECTED_AUTH_RESOURCES.contains(resource))
|
||||
throw new UnauthorizedException(String.format("Resource %s is inaccessible", resource));
|
||||
ensureHasPermission(perm, resource);
|
||||
}
|
||||
|
||||
resource.add(columnFamily);
|
||||
Set<Permission> perms = DatabaseDescriptor.getAuthority().authorize(user, resource);
|
||||
public void ensureHasPermission(Permission perm, IResource resource) throws UnauthorizedException
|
||||
{
|
||||
for (IResource r : Resources.chain(resource))
|
||||
{
|
||||
if (authorize(r).contains(perm))
|
||||
return;
|
||||
}
|
||||
throw new UnauthorizedException(String.format("User %s has no %s permission on %s or any of its parents",
|
||||
user.username,
|
||||
perm,
|
||||
resource));
|
||||
}
|
||||
|
||||
hasAccess(user, perms, perm, resource);
|
||||
private void preventSystemKSSModification(String keyspace, Permission perm) throws UnauthorizedException
|
||||
{
|
||||
if (Schema.systemKeyspaceNames.contains(keyspace.toLowerCase()) && !perm.equals(Permission.SELECT))
|
||||
throw new UnauthorizedException(keyspace + " keyspace is not user-modifiable.");
|
||||
}
|
||||
|
||||
public boolean isLogged()
|
||||
|
|
@ -178,55 +179,7 @@ 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(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)));
|
||||
}
|
||||
|
||||
public void setCQLVersion(String str) throws InvalidRequestException
|
||||
|
|
@ -274,18 +227,26 @@ public class ClientState
|
|||
return new SemanticVersion[]{ cql, cql3 };
|
||||
}
|
||||
|
||||
public void grantPermission(Permission permission, String to, CFName on, boolean grantOption) throws UnauthorizedException, InvalidRequestException
|
||||
public Set<Permission> authorize(IResource resource)
|
||||
{
|
||||
DatabaseDescriptor.getAuthorityContainer().grant(user, permission, to, on, grantOption);
|
||||
return DatabaseDescriptor.getAuthorizer().authorize(user, resource);
|
||||
|
||||
}
|
||||
public void grantPermission(Set<Permission> permissions, IResource resource, String to)
|
||||
throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
DatabaseDescriptor.getAuthorizer().grant(user, permissions, resource, to);
|
||||
}
|
||||
|
||||
public void revokePermission(Permission permission, String from, CFName resource) throws UnauthorizedException, InvalidRequestException
|
||||
public void revokePermission(Set<Permission> permissions, IResource resource, String from)
|
||||
throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
DatabaseDescriptor.getAuthorityContainer().revoke(user, permission, from, resource);
|
||||
DatabaseDescriptor.getAuthorizer().revoke(user, permissions, resource, from);
|
||||
}
|
||||
|
||||
public ResultMessage listPermissions(String username) throws UnauthorizedException, InvalidRequestException
|
||||
public Set<PermissionDetails> listPermissions(Set<Permission> permissions, IResource resource, String of)
|
||||
throws UnauthorizedException, InvalidRequestException
|
||||
{
|
||||
return DatabaseDescriptor.getAuthorityContainer().listPermissions(username);
|
||||
return DatabaseDescriptor.getAuthorizer().listPermissions(user, permissions, resource, of);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,9 +30,6 @@ import com.google.common.base.Joiner;
|
|||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import org.apache.cassandra.db.filter.IDiskAtomFilter;
|
||||
import org.apache.cassandra.utils.Pair;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
|
@ -45,6 +42,7 @@ import org.apache.cassandra.cql.CQLStatement;
|
|||
import org.apache.cassandra.cql.QueryProcessor;
|
||||
import org.apache.cassandra.db.*;
|
||||
import org.apache.cassandra.db.context.CounterContext;
|
||||
import org.apache.cassandra.db.filter.IDiskAtomFilter;
|
||||
import org.apache.cassandra.db.filter.QueryPath;
|
||||
import org.apache.cassandra.db.marshal.MarshalException;
|
||||
import org.apache.cassandra.db.marshal.TimeUUIDType;
|
||||
|
|
@ -52,7 +50,6 @@ import org.apache.cassandra.dht.*;
|
|||
import org.apache.cassandra.exceptions.ReadTimeoutException;
|
||||
import org.apache.cassandra.exceptions.RequestExecutionException;
|
||||
import org.apache.cassandra.exceptions.RequestValidationException;
|
||||
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;
|
||||
|
|
@ -60,6 +57,7 @@ import org.apache.cassandra.service.*;
|
|||
import org.apache.cassandra.tracing.Tracing;
|
||||
import org.apache.cassandra.utils.ByteBufferUtil;
|
||||
import org.apache.cassandra.utils.FBUtilities;
|
||||
import org.apache.cassandra.utils.Pair;
|
||||
import org.apache.cassandra.utils.UUIDGen;
|
||||
import org.apache.thrift.TException;
|
||||
|
||||
|
|
@ -309,8 +307,10 @@ public class CassandraServer implements Cassandra.Iface
|
|||
|
||||
try
|
||||
{
|
||||
state().hasColumnFamilyAccess(column_parent.column_family, Permission.SELECT);
|
||||
return multigetSliceInternal(state().getKeyspace(), Collections.singletonList(key), column_parent, predicate, consistency_level).get(key);
|
||||
ClientState cState = state();
|
||||
String keyspace = cState.getKeyspace();
|
||||
state().hasColumnFamilyAccess(keyspace, column_parent.column_family, Permission.SELECT);
|
||||
return multigetSliceInternal(keyspace, Collections.singletonList(key), column_parent, predicate, consistency_level).get(key);
|
||||
}
|
||||
catch (RequestValidationException e)
|
||||
{
|
||||
|
|
@ -343,8 +343,10 @@ public class CassandraServer implements Cassandra.Iface
|
|||
|
||||
try
|
||||
{
|
||||
state().hasColumnFamilyAccess(column_parent.column_family, Permission.SELECT);
|
||||
return multigetSliceInternal(state().getKeyspace(), keys, column_parent, predicate, consistency_level);
|
||||
ClientState cState = state();
|
||||
String keyspace = cState.getKeyspace();
|
||||
cState.hasColumnFamilyAccess(keyspace, column_parent.column_family, Permission.SELECT);
|
||||
return multigetSliceInternal(keyspace, keys, column_parent, predicate, consistency_level);
|
||||
}
|
||||
catch (RequestValidationException e)
|
||||
{
|
||||
|
|
@ -392,8 +394,8 @@ public class CassandraServer implements Cassandra.Iface
|
|||
throws RequestValidationException, NotFoundException, UnavailableException, TimedOutException
|
||||
{
|
||||
ThriftClientState cState = state();
|
||||
cState.hasColumnFamilyAccess(column_path.column_family, Permission.SELECT);
|
||||
String keyspace = cState.getKeyspace();
|
||||
cState.hasColumnFamilyAccess(keyspace, column_path.column_family, Permission.SELECT);
|
||||
|
||||
CFMetaData metadata = ThriftValidation.validateColumnFamily(keyspace, column_path.column_family);
|
||||
ThriftValidation.validateColumnPath(metadata, column_path);
|
||||
|
|
@ -466,8 +468,9 @@ public class CassandraServer implements Cassandra.Iface
|
|||
try
|
||||
{
|
||||
ThriftClientState cState = state();
|
||||
cState.hasColumnFamilyAccess(column_parent.column_family, Permission.SELECT);
|
||||
Table table = Table.open(cState.getKeyspace());
|
||||
String keyspace = cState.getKeyspace();
|
||||
cState.hasColumnFamilyAccess(keyspace, column_parent.column_family, Permission.SELECT);
|
||||
Table table = Table.open(keyspace);
|
||||
ColumnFamilyStore cfs = table.getColumnFamilyStore(column_parent.column_family);
|
||||
|
||||
if (predicate.column_names != null)
|
||||
|
|
@ -569,8 +572,8 @@ public class CassandraServer implements Cassandra.Iface
|
|||
try
|
||||
{
|
||||
ThriftClientState cState = state();
|
||||
cState.hasColumnFamilyAccess(column_parent.column_family, Permission.SELECT);
|
||||
String keyspace = cState.getKeyspace();
|
||||
cState.hasColumnFamilyAccess(keyspace, column_parent.column_family, Permission.SELECT);
|
||||
|
||||
Map<ByteBuffer, Integer> counts = new HashMap<ByteBuffer, Integer>();
|
||||
Map<ByteBuffer, List<ColumnOrSuperColumn>> columnFamiliesMap = multigetSliceInternal(keyspace, keys, column_parent, predicate, consistency_level);
|
||||
|
|
@ -593,9 +596,10 @@ public class CassandraServer implements Cassandra.Iface
|
|||
throws RequestValidationException, UnavailableException, TimedOutException
|
||||
{
|
||||
ThriftClientState cState = state();
|
||||
cState.hasColumnFamilyAccess(column_parent.column_family, Permission.UPDATE);
|
||||
String keyspace = cState.getKeyspace();
|
||||
cState.hasColumnFamilyAccess(keyspace, column_parent.column_family, Permission.MODIFY);
|
||||
|
||||
CFMetaData metadata = ThriftValidation.validateColumnFamily(cState.getKeyspace(), column_parent.column_family, false);
|
||||
CFMetaData metadata = ThriftValidation.validateColumnFamily(keyspace, column_parent.column_family, false);
|
||||
ThriftValidation.validateKey(metadata, key);
|
||||
ThriftValidation.validateColumnParent(metadata, column_parent);
|
||||
// SuperColumn field is usually optional, but not when we're inserting
|
||||
|
|
@ -675,7 +679,7 @@ public class CassandraServer implements Cassandra.Iface
|
|||
// Avoid unneeded authorizations
|
||||
if (!(cfamsSeen.contains(cfName)))
|
||||
{
|
||||
cState.hasColumnFamilyAccess(cfName, Permission.UPDATE);
|
||||
cState.hasColumnFamilyAccess(keyspace, cfName, Permission.MODIFY);
|
||||
cfamsSeen.add(cfName);
|
||||
}
|
||||
|
||||
|
|
@ -794,15 +798,16 @@ public class CassandraServer implements Cassandra.Iface
|
|||
throws RequestValidationException, UnavailableException, TimedOutException
|
||||
{
|
||||
ThriftClientState cState = state();
|
||||
cState.hasColumnFamilyAccess(column_path.column_family, Permission.DELETE);
|
||||
String keyspace = cState.getKeyspace();
|
||||
cState.hasColumnFamilyAccess(keyspace, column_path.column_family, Permission.MODIFY);
|
||||
|
||||
CFMetaData metadata = ThriftValidation.validateColumnFamily(cState.getKeyspace(), column_path.column_family, isCommutativeOp);
|
||||
CFMetaData metadata = ThriftValidation.validateColumnFamily(keyspace, column_path.column_family, isCommutativeOp);
|
||||
ThriftValidation.validateKey(metadata, key);
|
||||
ThriftValidation.validateColumnPathOrParent(metadata, column_path);
|
||||
if (isCommutativeOp)
|
||||
ThriftConversion.fromThrift(consistency_level).validateCounterForWrite(metadata);
|
||||
|
||||
RowMutation rm = new RowMutation(cState.getKeyspace(), key);
|
||||
RowMutation rm = new RowMutation(keyspace, key);
|
||||
rm.delete(new QueryPath(column_path), timestamp);
|
||||
|
||||
if (isCommutativeOp)
|
||||
|
|
@ -875,20 +880,11 @@ public class CassandraServer implements Cassandra.Iface
|
|||
|
||||
public KsDef describe_keyspace(String table) throws NotFoundException, InvalidRequestException
|
||||
{
|
||||
try
|
||||
{
|
||||
state().hasKeyspaceAccess(table, Permission.DESCRIBE);
|
||||
KSMetaData ksm = Schema.instance.getTableDefinition(table);
|
||||
if (ksm == null)
|
||||
throw new NotFoundException();
|
||||
|
||||
KSMetaData ksm = Schema.instance.getTableDefinition(table);
|
||||
if (ksm == null)
|
||||
throw new NotFoundException();
|
||||
|
||||
return ksm.toThrift();
|
||||
}
|
||||
catch (RequestValidationException e)
|
||||
{
|
||||
throw ThriftConversion.toThrift(e);
|
||||
}
|
||||
return ksm.toThrift();
|
||||
}
|
||||
|
||||
public List<KeySlice> get_range_slices(ColumnParent column_parent, SlicePredicate predicate, KeyRange range, ConsistencyLevel consistency_level)
|
||||
|
|
@ -915,7 +911,7 @@ public class CassandraServer implements Cassandra.Iface
|
|||
|
||||
ThriftClientState cState = state();
|
||||
keyspace = cState.getKeyspace();
|
||||
cState.hasColumnFamilyAccess(column_parent.column_family, Permission.SELECT);
|
||||
cState.hasColumnFamilyAccess(keyspace, column_parent.column_family, Permission.SELECT);
|
||||
|
||||
metadata = ThriftValidation.validateColumnFamily(keyspace, column_parent.column_family);
|
||||
ThriftValidation.validateColumnParent(metadata, column_parent);
|
||||
|
|
@ -1002,7 +998,7 @@ public class CassandraServer implements Cassandra.Iface
|
|||
|
||||
ThriftClientState cState = state();
|
||||
String keyspace = cState.getKeyspace();
|
||||
cState.hasColumnFamilyAccess(column_family, Permission.SELECT);
|
||||
cState.hasColumnFamilyAccess(keyspace, column_family, Permission.SELECT);
|
||||
|
||||
CFMetaData metadata = ThriftValidation.validateColumnFamily(keyspace, column_family);
|
||||
ThriftValidation.validateKeyRange(metadata, null, range);
|
||||
|
|
@ -1101,8 +1097,8 @@ public class CassandraServer implements Cassandra.Iface
|
|||
try
|
||||
{
|
||||
ThriftClientState cState = state();
|
||||
cState.hasColumnFamilyAccess(column_parent.column_family, Permission.SELECT);
|
||||
String keyspace = cState.getKeyspace();
|
||||
cState.hasColumnFamilyAccess(keyspace, column_parent.column_family, Permission.SELECT);
|
||||
CFMetaData metadata = ThriftValidation.validateColumnFamily(keyspace, column_parent.column_family, false);
|
||||
ThriftValidation.validateColumnParent(metadata, column_parent);
|
||||
ThriftValidation.validatePredicate(metadata, column_parent, column_predicate);
|
||||
|
|
@ -1151,33 +1147,20 @@ public class CassandraServer implements Cassandra.Iface
|
|||
|
||||
public List<KsDef> describe_keyspaces() throws TException, InvalidRequestException
|
||||
{
|
||||
try
|
||||
Set<String> keyspaces = Schema.instance.getTables();
|
||||
List<KsDef> ksset = new ArrayList<KsDef>(keyspaces.size());
|
||||
for (String ks : keyspaces)
|
||||
{
|
||||
Set<String> keyspaces = Schema.instance.getTables();
|
||||
List<KsDef> ksset = new ArrayList<KsDef>(keyspaces.size());
|
||||
for (String ks : keyspaces)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
state().hasKeyspaceAccess(ks, Permission.DESCRIBE);
|
||||
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... ");
|
||||
}
|
||||
ksset.add(describe_keyspace(ks));
|
||||
}
|
||||
catch (NotFoundException nfe)
|
||||
{
|
||||
logger.info("Failed to find metadata for keyspace '" + ks + "'. Continuing... ");
|
||||
}
|
||||
return ksset;
|
||||
}
|
||||
catch (RequestValidationException e)
|
||||
{
|
||||
throw ThriftConversion.toThrift(e);
|
||||
}
|
||||
return ksset;
|
||||
}
|
||||
|
||||
public String describe_cluster_name() throws TException
|
||||
|
|
@ -1238,7 +1221,6 @@ public class CassandraServer implements Cassandra.Iface
|
|||
{
|
||||
try
|
||||
{
|
||||
// TODO: add keyspace authorization call post CASSANDRA-1425
|
||||
Token.TokenFactory tf = StorageService.getPartitioner().getTokenFactory();
|
||||
Range<Token> tr = new Range<Token>(tf.fromString(start_token), tf.fromString(end_token));
|
||||
List<Pair<Range<Token>, Long>> splits =
|
||||
|
|
@ -1289,7 +1271,9 @@ public class CassandraServer implements Cassandra.Iface
|
|||
|
||||
try
|
||||
{
|
||||
state().hasColumnFamilyAccess(cf_def.name, Permission.CREATE);
|
||||
ClientState cState = state();
|
||||
String keyspace = cState.getKeyspace();
|
||||
cState.hasKeyspaceAccess(keyspace, Permission.CREATE);
|
||||
cf_def.unsetId(); // explicitly ignore any id set by client (Hector likes to set zero)
|
||||
CFMetaData cfm = CFMetaData.fromThrift(cf_def);
|
||||
cfm.addDefaultIndexNames();
|
||||
|
|
@ -1311,8 +1295,9 @@ public class CassandraServer implements Cassandra.Iface
|
|||
|
||||
try
|
||||
{
|
||||
cState.hasColumnFamilyAccess(column_family, Permission.DROP);
|
||||
MigrationManager.announceColumnFamilyDrop(cState.getKeyspace(), column_family);
|
||||
String keyspace = cState.getKeyspace();
|
||||
cState.hasColumnFamilyAccess(keyspace, column_family, Permission.DROP);
|
||||
MigrationManager.announceColumnFamilyDrop(keyspace, column_family);
|
||||
return Schema.instance.getVersion().toString();
|
||||
}
|
||||
catch (RequestValidationException e)
|
||||
|
|
@ -1329,7 +1314,7 @@ public class CassandraServer implements Cassandra.Iface
|
|||
try
|
||||
{
|
||||
ThriftValidation.validateKeyspaceNotSystem(ks_def.name);
|
||||
state().hasKeyspaceAccess(ks_def.name, Permission.CREATE);
|
||||
state().hasAllKeyspacesAccess(Permission.CREATE);
|
||||
ThriftValidation.validateKeyspaceNotYetExisting(ks_def.name);
|
||||
|
||||
// generate a meaningful error if the user setup keyspace and/or column definition incorrectly
|
||||
|
|
@ -1415,7 +1400,7 @@ public class CassandraServer implements Cassandra.Iface
|
|||
if (oldCfm == null)
|
||||
throw new InvalidRequestException("Could not find column family definition to modify.");
|
||||
|
||||
state().hasColumnFamilyAccess(cf_def.name, Permission.ALTER);
|
||||
state().hasColumnFamilyAccess(cf_def.keyspace, cf_def.name, Permission.ALTER);
|
||||
|
||||
CFMetaData.applyImplicitDefaults(cf_def);
|
||||
CFMetaData cfm = CFMetaData.fromThrift(cf_def);
|
||||
|
|
@ -1435,11 +1420,12 @@ public class CassandraServer implements Cassandra.Iface
|
|||
|
||||
try
|
||||
{
|
||||
cState.hasColumnFamilyAccess(cfname, Permission.DELETE);
|
||||
String keyspace = cState.getKeyspace();
|
||||
cState.hasColumnFamilyAccess(keyspace, cfname, Permission.MODIFY);
|
||||
|
||||
if (startSessionIfRequested())
|
||||
{
|
||||
Tracing.instance().begin("truncate", ImmutableMap.of("cf", cfname, "ks", cState.getKeyspace()));
|
||||
Tracing.instance().begin("truncate", ImmutableMap.of("cf", cfname, "ks", keyspace));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1518,9 +1504,10 @@ public class CassandraServer implements Cassandra.Iface
|
|||
try
|
||||
{
|
||||
ClientState cState = state();
|
||||
cState.hasColumnFamilyAccess(column_parent.column_family, Permission.UPDATE);
|
||||
String keyspace = cState.getKeyspace();
|
||||
|
||||
cState.hasColumnFamilyAccess(keyspace, column_parent.column_family, Permission.MODIFY);
|
||||
|
||||
CFMetaData metadata = ThriftValidation.validateColumnFamily(keyspace, column_parent.column_family, true);
|
||||
ThriftValidation.validateKey(metadata, key);
|
||||
ThriftConversion.fromThrift(consistency_level).validateCounterForWrite(metadata);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This is a sample access file for SimpleAuthority. The format of
|
||||
# This is a sample access file for SimpleAuthorizer. The format of
|
||||
# this file is keyspace=users, where users is a comma delimited list of
|
||||
# authenticatable users from passwd.properties. This file contains
|
||||
# potentially sensitive information, keep this in mind when setting its
|
||||
|
|
|
|||
Loading…
Reference in New Issue