/* Copyright (c) 2015, 2016 Oracle and/or its affiliates. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "ngs_common/smart_ptr.h" #include "user_verification_helper.h" #include #include #include "mock/ngs_general.h" #include "mock/session.h" namespace xpl { const char *USER_NAME = "TEST"; const char *USER_IP = "100.20.20.10"; const longlong REQUIRE_SECURE_TRANSPORT = 0; const char *EXPECTED_HASH = "AABBCCDD"; const char *ACCOUNT_NOT_LOCKET = "N"; const longlong PASSWORD_NOT_EXPIRED = 0; const longlong DISCONECT_ON_EXPIRED = 0; const longlong IS_NOT_OFFLINE = 0; namespace test { using namespace ::testing; class Mock_hash_verification { public: MOCK_METHOD1(check_hash, bool (const std::string &)); }; class User_verification_test : public Test { public: User_verification_test() { m_hash_check = ngs::bind(&Mock_hash_verification::check_hash, &m_hash, ngs::placeholders::_1); m_mock_options.reset(new StrictMock()); m_options = ngs::static_pointer_cast(m_mock_options); m_sut.reset(new User_verification_helper(m_hash_check, m_options, ngs::Connection_tls)); } void setup_field_types(const char *value) { Field_value *filed_value = ngs::allocate_object(value, strlen(value)); Command_delegate::Field_type field_type = {MYSQL_TYPE_STRING}; m_row_data.fields.push_back(filed_value); m_field_types.push_back(field_type); } void setup_field_types(const longlong value) { Field_value *filed_value = ngs::allocate_object(value); Command_delegate::Field_type field_type = {MYSQL_TYPE_LONGLONG}; m_row_data.fields.push_back(filed_value); m_field_types.push_back(field_type); } void setup_db_user(const longlong secure_transport = REQUIRE_SECURE_TRANSPORT) { setup_field_types(secure_transport); setup_field_types(EXPECTED_HASH); setup_field_types(ACCOUNT_NOT_LOCKET); setup_field_types(PASSWORD_NOT_EXPIRED); setup_field_types(DISCONECT_ON_EXPIRED); setup_field_types(IS_NOT_OFFLINE); } void setup_no_ssl() { setup_field_types(""); setup_field_types(""); setup_field_types(""); setup_field_types(""); } void expect_execute_sql(ngs::Error_code error_code = ngs::Error_code()) { Buffering_command_delegate::Resultset result_set; result_set.push_back(m_row_data); EXPECT_CALL(m_sql_data_context, execute_sql_and_collect_results(_, _, _, _, _)) .WillOnce(DoAll( SetArgReferee<2>(m_field_types), SetArgReferee<3>(result_set), Return(error_code))); } StrictMock m_hash; StrictMock m_sql_data_context; ngs::function m_hash_check; ngs::shared_ptr m_options; ngs::shared_ptr > m_mock_options; Command_delegate::Field_types m_field_types; Row_data m_row_data; ngs::unique_ptr m_sut; }; TEST_F(User_verification_test, everything_matches_and_hash_is_right) { setup_db_user(); setup_no_ssl(); EXPECT_CALL(m_hash, check_hash(EXPECTED_HASH)).WillOnce(Return(true)); expect_execute_sql(); ngs::Error_code result = m_sut->verify_mysql_account( m_sql_data_context, USER_NAME, USER_IP); ASSERT_FALSE(result); } TEST_F(User_verification_test, forwards_error_from_query_execution) { const ngs::Error_code expected_error(ER_MUST_CHANGE_PASSWORD_LOGIN, ""); setup_db_user(); setup_no_ssl(); expect_execute_sql(expected_error); ngs::Error_code result = m_sut->verify_mysql_account( m_sql_data_context, USER_NAME, USER_IP); ASSERT_TRUE(result); ASSERT_EQ(expected_error, result); } TEST_F(User_verification_test, dont_match_anything_when_hash_isnt_right) { setup_db_user(); setup_no_ssl(); EXPECT_CALL(m_hash, check_hash(EXPECTED_HASH)).WillOnce(Return(false)); expect_execute_sql(); ngs::Error_code result = m_sut->verify_mysql_account( m_sql_data_context, USER_NAME, USER_IP); ASSERT_TRUE(result); ASSERT_EQ(ER_NO_SUCH_USER, result.error); } class User_verification_param_test: public User_verification_test, public WithParamInterface { }; TEST_P(User_verification_param_test, if_data_isnt_there_reject) { setup_db_user(); setup_no_ssl(); ngs::free_object(m_row_data.fields[GetParam()]); m_row_data.fields[GetParam()] = NULL; expect_execute_sql(); ngs::Error_code result = m_sut->verify_mysql_account( m_sql_data_context, USER_NAME, USER_IP); ASSERT_TRUE(result); ASSERT_EQ(ER_NO_SUCH_USER, result.error); } TEST_P(User_verification_param_test, if_had_wrong_type_reject) { setup_db_user(); setup_no_ssl(); m_field_types[GetParam()].type = MYSQL_TYPE_FLOAT; expect_execute_sql(); ngs::Error_code result = m_sut->verify_mysql_account( m_sql_data_context, USER_NAME, USER_IP); ASSERT_TRUE(result); ASSERT_EQ(ER_NO_SUCH_USER, result.error); } INSTANTIATE_TEST_CASE_P(Range_from_0_to_9, User_verification_param_test, Range(0, 9, 1)); struct Test_param_connection_type { Test_param_connection_type(const bool requires_secure, const ngs::Connection_type type) : m_requires_secure(requires_secure), m_type(type) { } bool m_requires_secure; ngs::Connection_type m_type; }; class User_verification_param_test_with_supported_combinations: public User_verification_test, public WithParamInterface { }; TEST_P(User_verification_param_test_with_supported_combinations, expect_result_on_given_connection_type) { const Test_param_connection_type ¶m = GetParam(); m_sut.reset(new User_verification_helper(m_hash_check, m_options, param.m_type)); EXPECT_CALL(m_hash, check_hash(EXPECTED_HASH)).WillRepeatedly(Return(true)); setup_db_user(param.m_requires_secure); setup_no_ssl(); expect_execute_sql(); ngs::Error_code result = m_sut->verify_mysql_account( m_sql_data_context, USER_NAME, USER_IP); ASSERT_FALSE(result); } INSTANTIATE_TEST_CASE_P(Supported_connection_type_require_transport_combinations, User_verification_param_test_with_supported_combinations, Values( Test_param_connection_type(false, ngs::Connection_tcpip), Test_param_connection_type(false, ngs::Connection_namedpipe), Test_param_connection_type(false, ngs::Connection_tls), Test_param_connection_type(false, ngs::Connection_unixsocket), Test_param_connection_type(true, ngs::Connection_unixsocket), Test_param_connection_type(true, ngs::Connection_tls))); class User_verification_param_test_with_unsupported_combinations : public User_verification_param_test_with_supported_combinations { }; TEST_P(User_verification_param_test_with_unsupported_combinations, expect_result_on_given_connection_type) { const Test_param_connection_type ¶m = GetParam(); m_sut.reset(new User_verification_helper(m_hash_check, m_options, param.m_type)); EXPECT_CALL(m_hash, check_hash(EXPECTED_HASH)).WillRepeatedly(Return(true)); setup_db_user(param.m_requires_secure); setup_no_ssl(); expect_execute_sql(); ngs::Error_code result = m_sut->verify_mysql_account( m_sql_data_context, USER_NAME, USER_IP); ASSERT_TRUE(result); ASSERT_EQ(ER_SECURE_TRANSPORT_REQUIRED, result.error); } INSTANTIATE_TEST_CASE_P(Unsupported_connection_type_require_transport_combinations, User_verification_param_test_with_unsupported_combinations, Values( Test_param_connection_type(true, ngs::Connection_tcpip), Test_param_connection_type(true, ngs::Connection_namedpipe))); } }