MXS-1461 Add local firewall filter tests

Only two now, more to be added.
This commit is contained in:
Johan Wikman
2017-11-15 14:41:28 +02:00
parent da989d636e
commit 8fba2a8049
3 changed files with 328 additions and 0 deletions

View File

@ -21,6 +21,10 @@ if(BISON_FOUND AND FLEX_FOUND)
target_link_libraries(dbfwchk maxscale-common MySQLCommon dbfwfilter-core)
install_executable(dbfwchk core)
if(BUILD_TESTS)
add_subdirectory(test)
endif()
else()
message(FATAL_ERROR "Could not find Bison or Flex: ${BISON_EXECUTABLE} ${FLEX_EXECUTABLE}")
endif()

View File

@ -0,0 +1,16 @@
add_executable(test_dbfwfilter
test_dbfwfilter.cc
filtermodule.cc
mock.cc
mock_backend.cc
mock_dcb.cc
mock_routersession.cc
mock_session.cc
mock_upstream.cc
module.cc
tempfile.cc
)
target_link_libraries(test_dbfwfilter maxscale-common)
add_test(test_dbfwfilter test_dbfwfilter)

View File

@ -0,0 +1,308 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
#include <cstdlib>
#include <memory>
#include <iostream>
#include <maxscale/log_manager.h>
#include "maxscale/filtermodule.hh"
#include "maxscale/queryclassifiermodule.hh"
#include "maxscale/mock/backend.hh"
#include "maxscale/mock/routersession.hh"
#include "maxscale/mock/session.hh"
#include "maxscale/mock/upstream.hh"
#include "tempfile.hh"
using namespace std;
using maxscale::FilterModule;
using maxscale::QueryClassifierModule;
namespace mock = maxscale::mock;
namespace
{
enum fw_action_t
{
FW_ACTION_ALLOW,
FW_ACTION_BLOCK
};
struct FW_TEST_CASE
{
const char* zStatement; /* The statement to test. */
const char* zUser; /* The user to test as. */
const char* zHost; /* The host of the user. */
fw_action_t result; /* The expected outcome. */
};
const char DEFAULT_HOST[] = "127.0.0.1";
const size_t N_MAX_CASES = 10;
struct FW_TEST
{
const char* zRules; /* The firewall rules. */
fw_action_t action; /* The firewall action. */
FW_TEST_CASE cases[N_MAX_CASES]; /* The test cases to execute using the above settings. */
} FIREWALL_TESTS[] =
{
{
"rule no_select_a match columns a\n"
"users bob@% match any rules no_select_a\n",
FW_ACTION_BLOCK,
{
{
"SELECT a FROM t",
"bob",
DEFAULT_HOST,
FW_ACTION_BLOCK
}
}
},
{
"rule only_length_on_a match not_function length columns a\n"
"users bob@% match any rules only_length_on_a\n",
FW_ACTION_BLOCK,
{
{
"SELECT a FROM t",
"bob",
DEFAULT_HOST,
FW_ACTION_ALLOW
}
}
}
};
const size_t N_FIREWALL_TESTS = sizeof(FIREWALL_TESTS) / sizeof(FIREWALL_TESTS[0]);
}
namespace
{
void log_success(const FW_TEST_CASE& c)
{
cout << "SUCCESS : Statement ";
if (c.result == FW_ACTION_ALLOW)
{
cout << "was expected to pass, and did pass." << endl;
}
else
{
cout << "was expected to be blocked, and was blocked." << endl;
}
}
void log_error(const FW_TEST_CASE& c)
{
cout << "ERROR : Statement ";
if (c.result == FW_ACTION_ALLOW)
{
cout << "was expected to pass, but did not pass." << endl;
}
else
{
cout << "was expected to be blocked, but was not blocked." << endl;
}
}
int test(FilterModule::Session& filter_session,
mock::RouterSession& router_session,
const FW_TEST_CASE& c)
{
int rv = 0;
cout << "STATEMENT: " << c.zStatement << endl;
cout << "CLIENT : " << c.zUser << "@" << c.zHost << endl;
GWBUF* pStatement = mock::create_com_query(c.zStatement);
filter_session.routeQuery(pStatement);
if (c.result == FW_ACTION_ALLOW)
{
if (!router_session.idle()) // Statement reached backend
{
router_session.respond(); // To discard. TODO: Provide router_session.discard().
log_success(c);
}
else
{
log_error(c);
rv = 1;
}
}
else
{
ss_dassert(c.result == FW_ACTION_BLOCK);
if (router_session.idle())
{
log_success(c);
}
else
{
router_session.respond();
log_error(c);
rv = 1;
}
}
return rv;
}
int test(FilterModule::Instance& filter_instance, const FW_TEST& t)
{
int rv = 0;
mock::OkBackend backend;
mock::RouterSession router_session(&backend);
for (size_t i = 0; i < N_MAX_CASES; ++i)
{
const FW_TEST_CASE& c = t.cases[i];
if (c.zStatement)
{
mock::Session session(c.zUser, c.zHost);
auto_ptr<FilterModule::Session> sFilter_session = filter_instance.newSession(&session);
if (sFilter_session.get())
{
router_session.set_as_downstream_on(sFilter_session.get());
mock::Upstream upstream;
upstream.set_as_upstream_on(*sFilter_session.get());
rv += test(*sFilter_session.get(), router_session, c);
}
else
{
++rv;
}
}
else
{
break;
}
}
return rv;
}
int test(FilterModule& filter_module)
{
int rv = 0;
for (size_t i = 0; i < N_FIREWALL_TESTS; ++i)
{
const FW_TEST& t = FIREWALL_TESTS[i];
const char* zAction = (t.action == FW_ACTION_ALLOW) ? "allow" : "block";
cout << "ACTION: " << zAction << endl;
cout << "RULES :\n" << t.zRules << endl;
TempFile file;
file.write(t.zRules);
MXS_CONFIG_PARAMETER action { (char*)"action", (char*)zAction, NULL };
MXS_CONFIG_PARAMETER rules = { (char*)"rules", (char*)file.name().c_str(), &action };
auto_ptr<FilterModule::Instance> sInstance = filter_module.createInstance("test", NULL, &rules);
if (sInstance.get())
{
rv += test(*sInstance.get(), t);
}
else
{
++rv;
}
cout << "---------\n" << endl;
}
return rv;
}
int run()
{
int rc = EXIT_FAILURE;
auto_ptr<FilterModule> sModule = FilterModule::load("dbfwfilter");
if (sModule.get())
{
if (maxscale::Module<void>::process_init())
{
if (maxscale::Module<void>::thread_init())
{
rc = test(*sModule.get());
maxscale::Module<void>::thread_finish();
}
else
{
cerr << "error: Could not perform thread initialization." << endl;
}
maxscale::Module<void>::process_finish();
}
else
{
cerr << "error: Could not perform process initialization." << endl;
}
}
else
{
cerr << "error: Could not load filter module." << endl;
}
return rc;
}
}
int main(int argc, char* argv[])
{
int rc = EXIT_FAILURE;
if (mxs_log_init(NULL, ".", MXS_LOG_TARGET_STDOUT))
{
if (qc_setup("qc_sqlite", QC_SQL_MODE_DEFAULT, NULL))
{
if (qc_process_init(QC_INIT_SELF))
{
rc = run();
qc_process_end(QC_INIT_SELF);
}
else
{
cerr << "error: Could not initialize query classifier." << endl;
}
}
else
{
cerr << "error: Could not setup query classifier." << endl;
}
}
return rc;
}