blob: f93fc3a9d2026fcb73960ac61f1de3f3fbacb6f2 [file]
/*
Copyright (c) DataStax, Inc.
Licensed 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.
*/
#include "integration.hpp"
#define CASS_LOWEST_SUPPORTED_PROTOCOL_VERSION CASS_PROTOCOL_VERSION_V3
#define CASS_HIGHEST_SUPPORTED_PROTOCOL_VERSION CASS_PROTOCOL_VERSION_V4
/**
* Authentication integration tests
*/
class AuthenticationTests : public Integration {
public:
void SetUp() {
// Call the parent setup function (override startup and session connection)
is_ccm_start_requested_ = false;
is_password_authenticator_ = true;
is_session_requested_ = false;
Integration::SetUp();
// Configure and start the CCM cluster for plain text authentication usage
ccm_->update_cluster_configuration("authenticator", "PasswordAuthenticator");
ccm_->start_cluster("-Dcassandra.superuser_setup_delay_ms=0");
cluster_ = default_cluster();
}
protected:
/**
* Establish a connection with the given protocol version and credentials
*
* @param protocol_version Protocol version to use
* @param username Username credentials
* @param password Password credentials
* @return Session instance
*/
Session connect_using_credentials(int protocol_version, const char* username,
const char* password) {
// Establish a connection using the protocol version
cluster_.with_protocol_version(protocol_version).with_credentials(username, password);
return cluster_.connect("", false);
}
/**
* Authenticator callback used in AuthenticatorSetErrorNullError test to
* assign a NULL error for CPP-368 validation
*
* @param authenticator Driver authenticator
* @oaram data Data associated with the callback
*/
static void handle_authenticator_initial(CassAuthenticator* authuenticator, void* data) {
cass_authenticator_set_error(authuenticator, NULL);
}
};
/**
* Perform plain text authentication session connections for all protocol
* versions
*
* This test will perform a plain text authentication using each protocol
* version and establish a session connection against a single node cluster.
*
* @test_category authentication
* @since core:1.0.0
* @expected_result Session will be established for all protocol versions
*/
CASSANDRA_INTEGRATION_TEST_F(AuthenticationTests, ProtocolVersions) {
CHECK_FAILURE;
// Iterate over all known/supported protocol versions
for (int i = CASS_LOWEST_SUPPORTED_PROTOCOL_VERSION; i <= CASS_HIGHEST_SUPPORTED_PROTOCOL_VERSION;
++i) {
// Establish a connection using the protocol version
Session session = connect_using_credentials(i, "cassandra", "cassandra");
ASSERT_EQ(CASS_OK, session.connect_error_code()) << session.connect_error_description();
// Execute a query against the schema keyspaces table
Result result = session.execute("SELECT * FROM " + system_schema_keyspaces_);
ASSERT_EQ(CASS_OK, result.error_code());
ASSERT_GT(result.row_count(), 0u);
}
}
/**
* Perform plain text authentication session connections for all protocol
* versions using invalid/empty credentials
*
* This test will perform a plain text authentication using each protocol
* version with invalid empty credentials and fail to establish a session
* connection against a single node cluster.
*
* @test_category authentication
* @since core:1.0.0
* @expected_result Session will not be established for all protocol versions
*/
CASSANDRA_INTEGRATION_TEST_F(AuthenticationTests, InvalidEmptyCredentials) {
CHECK_FAILURE;
logger_.add_critera("Key may not be empty");
logger_.add_critera("Password must not be null");
// CPP-968
//
// Ordering actually changes between Cassandra 3.0.x and 3.11.x. In 3.0.x we check for an empty
// username first (and thus return this error) while in 3.11.x we check for an empty password first
// (and thus return the "password must not be null" error above).
logger_.add_critera("Authentication ID must not be null");
// Iterate over all known/supported protocol versions
for (int i = CASS_LOWEST_SUPPORTED_PROTOCOL_VERSION; i <= CASS_HIGHEST_SUPPORTED_PROTOCOL_VERSION;
++i) {
/*
* This is a case that could be guarded in the API entry point, or error out
* in connection. However, auth is subject to major changes and this is just
* a simple form. This test serves to characterize what is there presently.
*/
Session session = connect_using_credentials(i, "", "");
ASSERT_EQ(session.connect_error_code(), CASS_ERROR_SERVER_BAD_CREDENTIALS);
ASSERT_GT(logger_.count(), 0u);
logger_.reset_count();
}
}
/**
* Perform plain text authentication session connections for all protocol
* versions using NULL username credentials
*
* This test will perform a plain text authentication using each protocol
* version with NULL username credentials and fail to establish a session
* connection against a single node cluster.
*
* @test_category authentication
* @since core:1.0.0
* @expected_result Session will not be established for all protocol versions
*/
CASSANDRA_INTEGRATION_TEST_F(AuthenticationTests, InvalidNullUsernameCredentials) {
CHECK_FAILURE;
logger_.add_critera("Key may not be empty");
logger_.add_critera("Authentication ID must not be null");
// Iterate over all known/supported protocol versions
for (int i = CASS_LOWEST_SUPPORTED_PROTOCOL_VERSION; i <= CASS_HIGHEST_SUPPORTED_PROTOCOL_VERSION;
++i) {
/*
* This is a case that could be guarded in the API entry point, or error out
* in connection. However, auth is subject to major changes and this is just
* a simple form. This test serves to characterize what is there presently.
*/
Session session = connect_using_credentials(i, NULL, "pass");
ASSERT_EQ(session.connect_error_code(), CASS_ERROR_SERVER_BAD_CREDENTIALS);
ASSERT_GT(logger_.count(), 0u);
logger_.reset_count();
}
}
/**
* Perform plain text authentication session connections for all protocol
* versions using NULL password credentials
*
* This test will perform a plain text authentication using each protocol
* version with NULL password credentials and fail to establish a session
* connection against a single node cluster.
*
* @test_category authentication
* @since core:1.0.0
* @expected_result Session will not be established for all protocol versions
*/
CASSANDRA_INTEGRATION_TEST_F(AuthenticationTests, InvalidNullPasswordCredentials) {
CHECK_FAILURE;
logger_.add_critera("and/or password are incorrect");
logger_.add_critera("Password must not be null");
// Iterate over all known/supported protocol versions
for (int i = CASS_LOWEST_SUPPORTED_PROTOCOL_VERSION; i <= CASS_HIGHEST_SUPPORTED_PROTOCOL_VERSION;
++i) {
/*
* This is a case that could be guarded in the API entry point, or error out
* in connection. However, auth is subject to major changes and this is just
* a simple form. This test serves to characterize what is there presently.
*/
Session session = connect_using_credentials(i, "user", NULL);
ASSERT_EQ(session.connect_error_code(), CASS_ERROR_SERVER_BAD_CREDENTIALS);
ASSERT_GT(logger_.count(), 0u);
logger_.reset_count();
}
}
/**
* Perform plain text authentication session connections for all protocol
* versions using "bad" credentials
*
* This test will perform a plain text authentication using each protocol
* version with "bad" credentials and fail to establish a session connection
* against a single node cluster.
*
* @test_category authentication
* @since core:1.0.0
* @expected_result Session will not be established for all protocol versions
*/
CASSANDRA_INTEGRATION_TEST_F(AuthenticationTests, BadCredentials) {
CHECK_FAILURE;
// Add the proper logging criteria (based on server version)
CCM::CassVersion cass_version = this->server_version_;
if (!Options::is_cassandra()) {
cass_version = static_cast<CCM::DseVersion>(cass_version).get_cass_version();
}
if (cass_version >= "3.10") {
logger_.add_critera("Provided username invalid and/or password are incorrect");
} else {
logger_.add_critera("Username and/or password are incorrect");
}
// Iterate over all known/supported protocol versions
for (int i = CASS_LOWEST_SUPPORTED_PROTOCOL_VERSION; i <= CASS_HIGHEST_SUPPORTED_PROTOCOL_VERSION;
++i) {
/*
* This is a case that could be guarded in the API entry point, or error out
* in connection. However, auth is subject to major changes and this is just
* a simple form. This test serves to characterize what is there presently.
*/
Session session = connect_using_credentials(i, "invalid", "invalid");
ASSERT_EQ(session.connect_error_code(), CASS_ERROR_SERVER_BAD_CREDENTIALS);
ASSERT_GE(logger_.count(), 1u);
logger_.reset_count();
}
}
/**
* Perform plain text authentication session connections using custom
* authenticator callback
*
* This test will perform a plain text authentication using the custom
* authenticator callback, assigning a NULL error, and fail to establish a
* session connection against a single node cluster.
*
* @jira_ticket: CPP-368
* @test_category authentication
* @since core:1.3.0
* @expected_result Session will not be established
*/
CASSANDRA_INTEGRATION_TEST_F(AuthenticationTests, AuthenticatorSetErrorNull) {
CHECK_FAILURE;
// Add the proper logging criteria (based on server version)
CCM::CassVersion cass_version = this->server_version_;
if (!Options::is_cassandra()) {
cass_version = static_cast<CCM::DseVersion>(cass_version).get_cass_version();
}
if (cass_version >= "3.10") {
logger_.add_critera("Provided username invalid and/or password are incorrect");
} else {
logger_.add_critera("Username and/or password are incorrect");
}
// Attempt to establish session connection using authentication callback
CassAuthenticatorCallbacks authentication_callbacks = {
AuthenticationTests::handle_authenticator_initial, NULL, NULL, NULL
};
cluster_.with_authenticator_callbacks(&authentication_callbacks, NULL, NULL);
Session session =
connect_using_credentials(CASS_HIGHEST_SUPPORTED_PROTOCOL_VERSION, "invalid", "invalid");
ASSERT_EQ(session.connect_error_code(), CASS_ERROR_SERVER_BAD_CREDENTIALS);
ASSERT_GE(logger_.count(), 1u);
}