Merge branch 'cassandra-3.0' into cassandra-3.X
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/9be467a2 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/9be467a2 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/9be467a2 Branch: refs/heads/trunk Commit: 9be467a22ab646c23710eb2b23dd21e5a46b07bd Parents: 49ce0a4 e4f840a Author: Sam Tunnicliffe <s...@beobal.com> Authored: Fri Oct 28 16:11:13 2016 +0100 Committer: Sam Tunnicliffe <s...@beobal.com> Committed: Fri Oct 28 16:17:52 2016 +0100 ---------------------------------------------------------------------- CHANGES.txt | 1 + .../cassandra/auth/CassandraAuthorizer.java | 13 ++++-- .../cassandra/auth/PasswordAuthenticator.java | 42 ++++++++++++++------ 3 files changed, 41 insertions(+), 15 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/9be467a2/CHANGES.txt ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/9be467a2/src/java/org/apache/cassandra/auth/CassandraAuthorizer.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/auth/CassandraAuthorizer.java index 8c3485d,7ffef27..7f44eef --- a/src/java/org/apache/cassandra/auth/CassandraAuthorizer.java +++ b/src/java/org/apache/cassandra/auth/CassandraAuthorizer.java @@@ -215,12 -209,20 +215,19 @@@ public class CassandraAuthorizer implem Lists.newArrayList(ByteBufferUtil.bytes(role.getRoleName()), ByteBufferUtil.bytes(resource.getName()))); + SelectStatement statement; // If it exists, read from the legacy user permissions table to handle the case where the cluster // is being upgraded and so is running with mixed versions of the authz schema - SelectStatement statement = Schema.instance.getCFMetaData(SchemaConstants.AUTH_KEYSPACE_NAME, USER_PERMISSIONS) == null - ? authorizeRoleStatement - : legacyAuthorizeRoleStatement; - if (Schema.instance.getCFMetaData(AuthKeyspace.NAME, USER_PERMISSIONS) == null) ++ if (Schema.instance.getCFMetaData(SchemaConstants.AUTH_KEYSPACE_NAME, USER_PERMISSIONS) == null) + statement = authorizeRoleStatement; + else + { + // If the permissions table was initialised only after the statement got prepared, re-prepare (CASSANDRA-12813) + if (legacyAuthorizeRoleStatement == null) + legacyAuthorizeRoleStatement = prepare(USERNAME, USER_PERMISSIONS); + statement = legacyAuthorizeRoleStatement; + } - - ResultMessage.Rows rows = statement.execute(QueryState.forInternalCalls(), options) ; + ResultMessage.Rows rows = statement.execute(QueryState.forInternalCalls(), options, System.nanoTime()); UntypedResultSet result = UntypedResultSet.create(rows.result); if (!result.isEmpty() && result.one().has(PERMISSIONS)) http://git-wip-us.apache.org/repos/asf/cassandra/blob/9be467a2/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java ---------------------------------------------------------------------- diff --cc src/java/org/apache/cassandra/auth/PasswordAuthenticator.java index b0317f3,ca610d1..4b667ae --- a/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java +++ b/src/java/org/apache/cassandra/auth/PasswordAuthenticator.java @@@ -86,56 -78,8 +86,52 @@@ public class PasswordAuthenticator impl { try { + String hash = cache.get(username); + if (!BCrypt.checkpw(password, hash)) + throw new AuthenticationException(String.format("Provided username %s and/or password are incorrect", username)); + + return new AuthenticatedUser(username); + } + catch (ExecutionException | UncheckedExecutionException e) + { + // the credentials were somehow invalid - either a non-existent role, or one without a defined password + if (e.getCause() instanceof NoSuchCredentialsException) + throw new AuthenticationException(String.format("Provided username %s and/or password are incorrect", username)); + + // an unanticipated exception occured whilst querying the credentials table + if (e.getCause() instanceof RequestExecutionException) + { + logger.trace("Error performing internal authentication", e); + throw new AuthenticationException(String.format("Error during authentication of user %s : %s", username, e.getMessage())); + } + + throw new RuntimeException(e); + } + } + + private String queryHashedPassword(String username) throws NoSuchCredentialsException + { + try + { - // If the legacy users table exists try to verify credentials there. This is to handle the case - // where the cluster is being upgraded and so is running with mixed versions of the authn tables - SelectStatement authenticationStatement = Schema.instance.getCFMetaData(SchemaConstants.AUTH_KEYSPACE_NAME, LEGACY_CREDENTIALS_TABLE) == null - ? authenticateStatement - : legacyAuthenticateStatement; + SelectStatement authenticationStatement = authenticationStatement(); - return doAuthenticate(username, password, authenticationStatement); + + ResultMessage.Rows rows = + authenticationStatement.execute(QueryState.forInternalCalls(), + QueryOptions.forInternalCalls(consistencyForRole(username), + Lists.newArrayList(ByteBufferUtil.bytes(username))), + System.nanoTime()); + + // If either a non-existent role name was supplied, or no credentials + // were found for that role we don't want to cache the result so we throw + // a specific, but unchecked, exception to keep LoadingCache happy. + if (rows.result.isEmpty()) + throw new NoSuchCredentialsException(); + + UntypedResultSet result = UntypedResultSet.create(rows.result); + if (!result.one().has(SALTED_HASH)) + throw new NoSuchCredentialsException(); + + return result.one().getString(SALTED_HASH); } catch (RequestExecutionException e) { @@@ -144,6 -88,23 +140,25 @@@ } } + /** + * If the legacy users table exists try to verify credentials there. This is to handle the case + * where the cluster is being upgraded and so is running with mixed versions of the authn tables + */ + private SelectStatement authenticationStatement() + { - if (Schema.instance.getCFMetaData(AuthKeyspace.NAME, LEGACY_CREDENTIALS_TABLE) == null) ++ if (Schema.instance.getCFMetaData(SchemaConstants.AUTH_KEYSPACE_NAME, LEGACY_CREDENTIALS_TABLE) == null) + return authenticateStatement; + else + { ++ // the statement got prepared, we to try preparing it again. + // If the credentials was initialised only after statement got prepared, re-prepare (CASSANDRA-12813). + if (legacyAuthenticateStatement == null) + prepareLegacyAuthenticateStatement(); + return legacyAuthenticateStatement; + } + } + ++ public Set<DataResource> protectedResources() { // Also protected by CassandraRoleManager, but the duplication doesn't hurt and is more explicit @@@ -162,18 -123,19 +177,21 @@@ AuthKeyspace.ROLES); authenticateStatement = prepare(query); - if (Schema.instance.getCFMetaData(AuthKeyspace.NAME, LEGACY_CREDENTIALS_TABLE) != null) + if (Schema.instance.getCFMetaData(SchemaConstants.AUTH_KEYSPACE_NAME, LEGACY_CREDENTIALS_TABLE) != null) - { - query = String.format("SELECT %s from %s.%s WHERE username = ?", - SALTED_HASH, - SchemaConstants.AUTH_KEYSPACE_NAME, - LEGACY_CREDENTIALS_TABLE); - legacyAuthenticateStatement = prepare(query); - } + prepareLegacyAuthenticateStatement(); + + cache = new CredentialsCache(this); } + private void prepareLegacyAuthenticateStatement() + { + String query = String.format("SELECT %s from %s.%s WHERE username = ?", + SALTED_HASH, - AuthKeyspace.NAME, ++ SchemaConstants.AUTH_KEYSPACE_NAME, + LEGACY_CREDENTIALS_TABLE); + legacyAuthenticateStatement = prepare(query); + } + public AuthenticatedUser legacyAuthenticate(Map<String, String> credentials) throws AuthenticationException { String username = credentials.get(USERNAME_KEY);