Author: eevans Date: Mon Mar 22 23:11:16 2010 New Revision: 926388 URL: http://svn.apache.org/viewvc?rev=926388&view=rev Log: access levels for Thrift authorization
Patch by Ted Zlatanov and eevans for CASSANDRA-900 Modified: cassandra/trunk/interface/cassandra.thrift cassandra/trunk/src/java/org/apache/cassandra/auth/AllowAllAuthenticator.java cassandra/trunk/src/java/org/apache/cassandra/auth/IAuthenticator.java cassandra/trunk/src/java/org/apache/cassandra/auth/SimpleAuthenticator.java cassandra/trunk/src/java/org/apache/cassandra/thrift/CassandraServer.java Modified: cassandra/trunk/interface/cassandra.thrift URL: http://svn.apache.org/viewvc/cassandra/trunk/interface/cassandra.thrift?rev=926388&r1=926387&r2=926388&view=diff ============================================================================== --- cassandra/trunk/interface/cassandra.thrift (original) +++ cassandra/trunk/interface/cassandra.thrift Mon Mar 22 23:11:16 2010 @@ -305,7 +305,7 @@ struct AuthenticationRequest { service Cassandra { # auth methods - void login(1: required string keyspace, 2:required AuthenticationRequest auth_request) throws (1:AuthenticationException authnx, 2:AuthorizationException authzx), + AccessLevel login(1: required string keyspace, 2:required AuthenticationRequest auth_request) throws (1:AuthenticationException authnx, 2:AuthorizationException authzx), # retrieval methods Modified: cassandra/trunk/src/java/org/apache/cassandra/auth/AllowAllAuthenticator.java URL: http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/auth/AllowAllAuthenticator.java?rev=926388&r1=926387&r2=926388&view=diff ============================================================================== --- cassandra/trunk/src/java/org/apache/cassandra/auth/AllowAllAuthenticator.java (original) +++ cassandra/trunk/src/java/org/apache/cassandra/auth/AllowAllAuthenticator.java Mon Mar 22 23:11:16 2010 @@ -21,6 +21,7 @@ package org.apache.cassandra.auth; */ +import org.apache.cassandra.thrift.AccessLevel; import org.apache.cassandra.thrift.AuthenticationException; import org.apache.cassandra.thrift.AuthenticationRequest; import org.apache.cassandra.thrift.AuthorizationException; @@ -28,8 +29,9 @@ import org.apache.cassandra.thrift.Autho public class AllowAllAuthenticator implements IAuthenticator { @Override - public void login(String keyspace, AuthenticationRequest authRequest) throws AuthenticationException, AuthorizationException + public AccessLevel login(String keyspace, AuthenticationRequest authRequest) throws AuthenticationException, AuthorizationException { // do nothing, allow anything + return AccessLevel.FULL; } } Modified: cassandra/trunk/src/java/org/apache/cassandra/auth/IAuthenticator.java URL: http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/auth/IAuthenticator.java?rev=926388&r1=926387&r2=926388&view=diff ============================================================================== --- cassandra/trunk/src/java/org/apache/cassandra/auth/IAuthenticator.java (original) +++ cassandra/trunk/src/java/org/apache/cassandra/auth/IAuthenticator.java Mon Mar 22 23:11:16 2010 @@ -21,11 +21,12 @@ package org.apache.cassandra.auth; */ +import org.apache.cassandra.thrift.AccessLevel; import org.apache.cassandra.thrift.AuthenticationException; import org.apache.cassandra.thrift.AuthenticationRequest; import org.apache.cassandra.thrift.AuthorizationException; public interface IAuthenticator { - public void login(String keyspace, AuthenticationRequest auth_request) throws AuthenticationException, AuthorizationException; + public AccessLevel login(String keyspace, AuthenticationRequest auth_request) throws AuthenticationException, AuthorizationException; } Modified: cassandra/trunk/src/java/org/apache/cassandra/auth/SimpleAuthenticator.java URL: http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/auth/SimpleAuthenticator.java?rev=926388&r1=926387&r2=926388&view=diff ============================================================================== --- cassandra/trunk/src/java/org/apache/cassandra/auth/SimpleAuthenticator.java (original) +++ cassandra/trunk/src/java/org/apache/cassandra/auth/SimpleAuthenticator.java Mon Mar 22 23:11:16 2010 @@ -26,6 +26,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Properties; +import org.apache.cassandra.thrift.AccessLevel; import org.apache.cassandra.thrift.AuthenticationException; import org.apache.cassandra.thrift.AuthenticationRequest; import org.apache.cassandra.thrift.AuthorizationException; @@ -44,7 +45,7 @@ public class SimpleAuthenticator impleme }; @Override - public void login(String keyspace, AuthenticationRequest authRequest) throws AuthenticationException, AuthorizationException + public AccessLevel login(String keyspace, AuthenticationRequest authRequest) throws AuthenticationException, AuthorizationException { String pmode_plain = System.getProperty(PMODE_PROPERTY); PasswordMode mode = PasswordMode.PLAIN; @@ -118,7 +119,7 @@ public class SimpleAuthenticator impleme // if we're here, the authentication succeeded. Now let's see if the user is authorized for this keyspace. String afilename = System.getProperty(ACCESS_FILENAME_PROPERTY); - boolean authorized = false; + AccessLevel authorized = AccessLevel.NONE; try { FileInputStream in = new FileInputStream(afilename); @@ -134,7 +135,7 @@ public class SimpleAuthenticator impleme if (null == props.getProperty(keyspace)) throw new AuthorizationException(authorizationErrorMessage(keyspace, username)); for (String allow : props.getProperty(keyspace).split(",")) { - if (allow.equals(username)) authorized = true; + if (allow.equals(username)) authorized = AccessLevel.FULL; } } catch (FileNotFoundException e) @@ -150,7 +151,9 @@ public class SimpleAuthenticator impleme throw new RuntimeException("Unexpected authorization problem", e); } - if (!authorized) throw new AuthorizationException(authorizationErrorMessage(keyspace, username)); + if (authorized == AccessLevel.NONE) throw new AuthorizationException(authorizationErrorMessage(keyspace, username)); + + return authorized; } static String authorizationErrorMessage(String keyspace, String username) Modified: cassandra/trunk/src/java/org/apache/cassandra/thrift/CassandraServer.java URL: http://svn.apache.org/viewvc/cassandra/trunk/src/java/org/apache/cassandra/thrift/CassandraServer.java?rev=926388&r1=926387&r2=926388&view=diff ============================================================================== --- cassandra/trunk/src/java/org/apache/cassandra/thrift/CassandraServer.java (original) +++ cassandra/trunk/src/java/org/apache/cassandra/thrift/CassandraServer.java Mon Mar 22 23:11:16 2010 @@ -53,12 +53,12 @@ public class CassandraServer implements private final static List<Column> EMPTY_SUBCOLUMNS = Collections.emptyList(); // will be set only by login() - private ThreadLocal<Boolean> loginDone = new ThreadLocal<Boolean>() + private ThreadLocal<AccessLevel> loginDone = new ThreadLocal<AccessLevel>() { @Override - protected Boolean initialValue() + protected AccessLevel initialValue() { - return false; + return AccessLevel.NONE; } }; @@ -215,9 +215,8 @@ public class CassandraServer implements { if (logger.isDebugEnabled()) logger.debug("get_slice"); - - checkLoginDone(); - + + checkLoginAuthorized(AccessLevel.READONLY); return multigetSliceInternal(keyspace, Arrays.asList(key), column_parent, predicate, consistency_level).get(key); } @@ -227,7 +226,7 @@ public class CassandraServer implements if (logger.isDebugEnabled()) logger.debug("multiget_slice"); - checkLoginDone(); + checkLoginAuthorized(AccessLevel.READONLY); return multigetSliceInternal(keyspace, keys, column_parent, predicate, consistency_level); } @@ -266,7 +265,7 @@ public class CassandraServer implements if (logger.isDebugEnabled()) logger.debug("get"); - checkLoginDone(); + checkLoginAuthorized(AccessLevel.READONLY); ColumnOrSuperColumn column = multigetInternal(table, Arrays.asList(key), column_path, consistency_level).get(key); if (!column.isSetColumn() && !column.isSetSuper_column()) @@ -283,7 +282,7 @@ public class CassandraServer implements if (logger.isDebugEnabled()) logger.debug("multiget"); - checkLoginDone(); + checkLoginAuthorized(AccessLevel.READONLY); return multigetInternal(table, keys, column_path, consistency_level); } @@ -328,7 +327,7 @@ public class CassandraServer implements if (logger.isDebugEnabled()) logger.debug("get_count"); - checkLoginDone(); + checkLoginAuthorized(AccessLevel.READONLY); SliceRange range = new SliceRange(ArrayUtils.EMPTY_BYTE_ARRAY, ArrayUtils.EMPTY_BYTE_ARRAY, false, Integer.MAX_VALUE); SlicePredicate predicate = new SlicePredicate().setSlice_range(range); @@ -341,7 +340,7 @@ public class CassandraServer implements if (logger.isDebugEnabled()) logger.debug("insert"); - checkLoginDone(); + checkLoginAuthorized(AccessLevel.READWRITE); ThriftValidation.validateKey(key); ThriftValidation.validateColumnPath(table, column_path); @@ -364,7 +363,7 @@ public class CassandraServer implements if (logger.isDebugEnabled()) logger.debug("batch_insert"); - checkLoginDone(); + checkLoginAuthorized(AccessLevel.READWRITE); ThriftValidation.validateKey(key); @@ -385,7 +384,25 @@ public class CassandraServer implements if (logger.isDebugEnabled()) logger.debug("batch_mutate"); - checkLoginDone(); + AccessLevel needed = AccessLevel.READWRITE; + + TOP: + for (Map<String, List<Mutation>> submap : mutation_map.values()) + { + for (List<Mutation> mutations: submap.values()) + { + for (Mutation m : mutations) + { + if (m.isSetDeletion()) + { + needed = AccessLevel.FULL; + break TOP; + } + } + } + } + + checkLoginAuthorized(needed); List<RowMutation> rowMutations = new ArrayList<RowMutation>(); for (Map.Entry<String, Map<String, List<Mutation>>> mutationEntry: mutation_map.entrySet()) @@ -428,7 +445,7 @@ public class CassandraServer implements if (logger.isDebugEnabled()) logger.debug("remove"); - checkLoginDone(); + checkLoginAuthorized(AccessLevel.FULL); ThriftValidation.validateKey(key); ThriftValidation.validateColumnPathOrParent(table, column_path); @@ -559,7 +576,7 @@ public class CassandraServer implements private List<KeySlice> getRangeSlicesInternal(String keyspace, ColumnParent column_parent, SlicePredicate predicate, KeyRange range, ConsistencyLevel consistency_level) throws InvalidRequestException, UnavailableException, TimedOutException { - checkLoginDone(); + checkLoginAuthorized(AccessLevel.READONLY); ThriftValidation.validateColumnParent(keyspace, column_parent); ThriftValidation.validatePredicate(keyspace, column_parent, predicate); @@ -643,21 +660,22 @@ public class CassandraServer implements return splits; } - public void login(String keyspace, AuthenticationRequest auth_request) throws AuthenticationException, AuthorizationException, TException + public AccessLevel login(String keyspace, AuthenticationRequest auth_request) throws AuthenticationException, AuthorizationException, TException { - DatabaseDescriptor.getAuthenticator().login(keyspace, auth_request); - loginDone.set(true); + AccessLevel level = DatabaseDescriptor.getAuthenticator().login(keyspace, auth_request); + loginDone.set(level); + return level; } - protected void checkLoginDone() throws InvalidRequestException + protected void checkLoginAuthorized(AccessLevel level) throws InvalidRequestException { - // FIXME: This disables the "you must call login()" requirement when the configured + // FIXME: This disables access level checks when the configured // authenticator is AllowAllAuthenticator. This is a temporary measure until CASSANDRA-714 is complete. if (DatabaseDescriptor.getAuthenticator() instanceof AllowAllAuthenticator) return; - if (!loginDone.get()) throw new InvalidRequestException("Login is required before any other API calls"); + if (loginDone.get() == AccessLevel.NONE) throw new InvalidRequestException("Your login access level was not sufficient to do " + level + " operations"); + if (loginDone.get().getValue() >= level.getValue()) throw new InvalidRequestException("Your login access level was not sufficient to do " + level + " operations"); } - // main method moved to CassandraDaemon }