You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by al...@apache.org on 2013/05/26 04:40:54 UTC

git commit: Reuse prepared statements in hot auth queries

Updated Branches:
  refs/heads/cassandra-1.2 234d4cfeb -> 1b5edee00


Reuse prepared statements in hot auth queries

patch by Aleksey Yeschenko; reviewed by Jonathan Ellis for
CASSANDRA-5594


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

Branch: refs/heads/cassandra-1.2
Commit: 1b5edee00faeb03ec4fc5dff779a031f7093241b
Parents: 234d4cf
Author: Aleksey Yeschenko <al...@apache.org>
Authored: Sun May 26 05:40:11 2013 +0300
Committer: Aleksey Yeschenko <al...@apache.org>
Committed: Sun May 26 05:40:11 2013 +0300

----------------------------------------------------------------------
 CHANGES.txt                                        |    1 +
 src/java/org/apache/cassandra/auth/Auth.java       |   62 +++++++++-----
 .../apache/cassandra/auth/CassandraAuthorizer.java |   37 +++++++--
 .../cassandra/auth/PasswordAuthenticator.java      |   40 +++++++---
 4 files changed, 100 insertions(+), 40 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/1b5edee0/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index d3973b6..7b1970c 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -7,6 +7,7 @@
  * Improve batchlog replay behavior and hint ttl handling (CASSANDRA-5314)
  * Exclude localTimestamp from validation for tombstones (CASSANDRA-5398)
  * cqlsh: add custom prompt support (CASSANDRA-5539)
+ * Reuse prepared statements in hot auth queries (CASSANDRA-5594)
 Merged from 1.1:
  * Remove buggy thrift max message length option (CASSANDRA-5529)
  * Fix NPE in Pig's widerow mode (CASSANDRA-5488)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1b5edee0/src/java/org/apache/cassandra/auth/Auth.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/Auth.java b/src/java/org/apache/cassandra/auth/Auth.java
index 7ec5a33..c561aab 100644
--- a/src/java/org/apache/cassandra/auth/Auth.java
+++ b/src/java/org/apache/cassandra/auth/Auth.java
@@ -20,6 +20,7 @@ package org.apache.cassandra.auth;
 import java.util.concurrent.TimeUnit;
 
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -29,12 +30,14 @@ import org.apache.cassandra.config.KSMetaData;
 import org.apache.cassandra.config.Schema;
 import org.apache.cassandra.cql3.UntypedResultSet;
 import org.apache.cassandra.cql3.QueryProcessor;
+import org.apache.cassandra.cql3.statements.SelectStatement;
 import org.apache.cassandra.db.ConsistencyLevel;
 import org.apache.cassandra.exceptions.RequestExecutionException;
+import org.apache.cassandra.exceptions.RequestValidationException;
 import org.apache.cassandra.locator.SimpleStrategy;
-import org.apache.cassandra.service.IMigrationListener;
-import org.apache.cassandra.service.MigrationManager;
-import org.apache.cassandra.service.StorageService;
+import org.apache.cassandra.service.*;
+import org.apache.cassandra.transport.messages.ResultMessage;
+import org.apache.cassandra.utils.ByteBufferUtil;
 import org.apache.cassandra.utils.FBUtilities;
 
 public class Auth
@@ -57,6 +60,8 @@ public class Auth
                                                                 USERS_CF,
                                                                 90 * 24 * 60 * 60); // 3 months.
 
+    private static SelectStatement selectUserStatement;
+
     /**
      * Checks if the username is stored in AUTH_KS.USERS_CF.
      *
@@ -65,15 +70,7 @@ public class Auth
      */
     public static boolean isExistingUser(String username)
     {
-        String query = String.format("SELECT * FROM %s.%s WHERE name = '%s'", AUTH_KS, USERS_CF, escape(username));
-        try
-        {
-            return !QueryProcessor.process(query, consistencyForUser(username)).isEmpty();
-        }
-        catch (RequestExecutionException e)
-        {
-            throw new RuntimeException(e);
-        }
+        return !selectUser(username).isEmpty();
     }
 
     /**
@@ -84,16 +81,8 @@ public class Auth
      */
     public static boolean isSuperuser(String username)
     {
-        String query = String.format("SELECT super FROM %s.%s WHERE name = '%s'", AUTH_KS, USERS_CF, escape(username));
-        try
-        {
-            UntypedResultSet result = QueryProcessor.process(query, consistencyForUser(username));
-            return !result.isEmpty() && result.one().getBoolean("super");
-        }
-        catch (RequestExecutionException e)
-        {
-            throw new RuntimeException(e);
-        }
+        UntypedResultSet result = selectUser(username);
+        return !result.isEmpty() && result.one().getBoolean("super");
     }
 
     /**
@@ -157,6 +146,16 @@ public class Auth
                                           SUPERUSER_SETUP_DELAY,
                                           TimeUnit.MILLISECONDS);
         }
+
+        try
+        {
+            String query = String.format("SELECT * FROM %s.%s WHERE name = ?", AUTH_KS, USERS_CF);
+            selectUserStatement = (SelectStatement) QueryProcessor.parseStatement(query).prepare().statement;
+        }
+        catch (RequestValidationException e)
+        {
+            throw new AssertionError(e); // not supposed to happen
+        }
     }
 
     // Only use QUORUM cl for the default superuser.
@@ -227,6 +226,25 @@ public class Auth
         return StringUtils.replace(name, "'", "''");
     }
 
+    private static UntypedResultSet selectUser(String username)
+    {
+        try
+        {
+            ResultMessage.Rows rows = selectUserStatement.execute(consistencyForUser(username),
+                                                                  new QueryState(new ClientState(true)),
+                                                                  Lists.newArrayList(ByteBufferUtil.bytes(username)));
+            return new UntypedResultSet(rows.result);
+        }
+        catch (RequestValidationException e)
+        {
+            throw new AssertionError(e); // not supposed to happen
+        }
+        catch (RequestExecutionException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
     /**
      * IMigrationListener implementation that cleans up permissions on dropped resources.
      */

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1b5edee0/src/java/org/apache/cassandra/auth/CassandraAuthorizer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/CassandraAuthorizer.java b/src/java/org/apache/cassandra/auth/CassandraAuthorizer.java
index 0518734..396be71 100644
--- a/src/java/org/apache/cassandra/auth/CassandraAuthorizer.java
+++ b/src/java/org/apache/cassandra/auth/CassandraAuthorizer.java
@@ -28,9 +28,14 @@ import org.slf4j.LoggerFactory;
 import org.apache.cassandra.config.Schema;
 import org.apache.cassandra.cql3.UntypedResultSet;
 import org.apache.cassandra.cql3.QueryProcessor;
+import org.apache.cassandra.cql3.statements.SelectStatement;
 import org.apache.cassandra.db.ConsistencyLevel;
 import org.apache.cassandra.db.marshal.UTF8Type;
 import org.apache.cassandra.exceptions.*;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.service.QueryState;
+import org.apache.cassandra.transport.messages.ResultMessage;
+import org.apache.cassandra.utils.ByteBufferUtil;
 
 /**
  * CassandraAuthorizer is an IAuthorizer implementation that keeps
@@ -55,20 +60,26 @@ public class CassandraAuthorizer implements IAuthorizer
                                                                       PERMISSIONS_CF,
                                                                       90 * 24 * 60 * 60); // 3 months.
 
+    private SelectStatement authorizeStatement;
+
     // Returns every permission on the resource granted to the user.
     public Set<Permission> authorize(AuthenticatedUser user, IResource resource)
     {
         if (user.isSuper())
             return Permission.ALL;
 
-        UntypedResultSet rows;
+        UntypedResultSet result;
         try
         {
-            rows = process(String.format("SELECT permissions FROM %s.%s WHERE username = '%s' AND resource = '%s'",
-                                         Auth.AUTH_KS,
-                                         PERMISSIONS_CF,
-                                         escape(user.getName()),
-                                         escape(resource.getName())));
+            ResultMessage.Rows rows = authorizeStatement.execute(ConsistencyLevel.ONE,
+                                                                 new QueryState(new ClientState(true)),
+                                                                 Lists.newArrayList(ByteBufferUtil.bytes(user.getName()),
+                                                                                    ByteBufferUtil.bytes(resource.getName())));
+            result = new UntypedResultSet(rows.result);
+        }
+        catch (RequestValidationException e)
+        {
+            throw new AssertionError(e); // not supposed to happen
         }
         catch (RequestExecutionException e)
         {
@@ -76,11 +87,11 @@ public class CassandraAuthorizer implements IAuthorizer
             return Permission.NONE;
         }
 
-        if (rows.isEmpty() || !rows.one().has(PERMISSIONS))
+        if (result.isEmpty() || !result.one().has(PERMISSIONS))
             return Permission.NONE;
 
         Set<Permission> permissions = EnumSet.noneOf(Permission.class);
-        for (String perm : rows.one().getSet(PERMISSIONS, UTF8Type.instance))
+        for (String perm : result.one().getSet(PERMISSIONS, UTF8Type.instance))
             permissions.add(Permission.valueOf(perm));
         return permissions;
     }
@@ -239,6 +250,16 @@ public class CassandraAuthorizer implements IAuthorizer
                 throw new AssertionError(e);
             }
         }
+
+        try
+        {
+            String query = String.format("SELECT permissions FROM %s.%s WHERE username = ? AND resource = ?", Auth.AUTH_KS, PERMISSIONS_CF);
+            authorizeStatement = (SelectStatement) QueryProcessor.parseStatement(query).prepare().statement;
+        }
+        catch (RequestValidationException e)
+        {
+            throw new AssertionError(e); // not supposed to happen
+        }
     }
 
     // We only worry about one character ('). Make sure it's properly escaped.

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1b5edee0/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java b/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
index 01adb1b..bcbdd29 100644
--- a/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
+++ b/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java
@@ -22,6 +22,7 @@ import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
 import org.apache.commons.lang.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -30,12 +31,14 @@ import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.config.Schema;
 import org.apache.cassandra.cql3.UntypedResultSet;
 import org.apache.cassandra.cql3.QueryProcessor;
+import org.apache.cassandra.cql3.statements.SelectStatement;
 import org.apache.cassandra.db.ConsistencyLevel;
-import org.apache.cassandra.exceptions.AuthenticationException;
-import org.apache.cassandra.exceptions.ConfigurationException;
-import org.apache.cassandra.exceptions.InvalidRequestException;
-import org.apache.cassandra.exceptions.RequestExecutionException;
+import org.apache.cassandra.exceptions.*;
+import org.apache.cassandra.service.ClientState;
+import org.apache.cassandra.service.QueryState;
 import org.apache.cassandra.service.StorageService;
+import org.apache.cassandra.transport.messages.ResultMessage;
+import org.apache.cassandra.utils.ByteBufferUtil;
 import org.apache.cassandra.utils.FBUtilities;
 import org.mindrot.jbcrypt.BCrypt;
 
@@ -68,6 +71,8 @@ public class PasswordAuthenticator implements IAuthenticator
                                                                       CREDENTIALS_CF,
                                                                       90 * 24 * 60 * 60); // 3 months.
 
+    private SelectStatement authenticateStatement;
+
     // No anonymous access.
     public boolean requireAuthentication()
     {
@@ -98,12 +103,14 @@ public class PasswordAuthenticator implements IAuthenticator
         UntypedResultSet result;
         try
         {
-            result = process(String.format("SELECT %s FROM %s.%s WHERE username = '%s'",
-                                           SALTED_HASH,
-                                           Auth.AUTH_KS,
-                                           CREDENTIALS_CF,
-                                           escape(username)),
-                             consistencyForUser(username));
+            ResultMessage.Rows rows = authenticateStatement.execute(consistencyForUser(username),
+                                                                    new QueryState(new ClientState(true)),
+                                                                    Lists.newArrayList(ByteBufferUtil.bytes(username)));
+            result = new UntypedResultSet(rows.result);
+        }
+        catch (RequestValidationException e)
+        {
+            throw new AssertionError(e); // not supposed to happen
         }
         catch (RequestExecutionException e)
         {
@@ -174,6 +181,19 @@ public class PasswordAuthenticator implements IAuthenticator
                                           Auth.SUPERUSER_SETUP_DELAY,
                                           TimeUnit.MILLISECONDS);
         }
+
+        try
+        {
+            String query = String.format("SELECT %s FROM %s.%s WHERE username = ?",
+                                         SALTED_HASH,
+                                         Auth.AUTH_KS,
+                                         CREDENTIALS_CF);
+            authenticateStatement = (SelectStatement) QueryProcessor.parseStatement(query).prepare().statement;
+        }
+        catch (RequestValidationException e)
+        {
+            throw new AssertionError(e); // not supposed to happen
+        }
     }
 
     private void setupCredentialsTable()