Masking: Add proper rule matching and data rewriting
This commit is contained in:
parent
f74d267766
commit
224d0bcb35
@ -12,7 +12,9 @@
|
||||
*/
|
||||
|
||||
#include "maskingrules.hh"
|
||||
#include <algorithm>
|
||||
#include <errno.h>
|
||||
#include <functional>
|
||||
#include <string.h>
|
||||
#include <maxscale/debug.h>
|
||||
#include <maxscale/jansson.hh>
|
||||
@ -612,6 +614,117 @@ auto_ptr<MaskingRules::Rule> MaskingRules::Rule::create_from(json_t* pRule)
|
||||
return sRule;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class AccountMatcher : std::unary_function<MaskingRules::Rule::SAccount, bool>
|
||||
{
|
||||
public:
|
||||
AccountMatcher(const char* zUser, const char* zHost)
|
||||
: m_zUser(zUser)
|
||||
, m_zHost(zHost)
|
||||
{}
|
||||
|
||||
bool operator()(const MaskingRules::Rule::SAccount& sAccount)
|
||||
{
|
||||
return sAccount->matches(m_zUser, m_zHost);
|
||||
}
|
||||
|
||||
private:
|
||||
const char* m_zUser;
|
||||
const char* m_zHost;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
bool MaskingRules::Rule::matches(const ComQueryResponse::ColumnDef& column_def,
|
||||
const char* zUser,
|
||||
const char* zHost) const
|
||||
{
|
||||
bool match =
|
||||
(m_column == column_def.org_name()) &&
|
||||
(m_table.empty() || (m_table == column_def.org_table())) &&
|
||||
(m_database.empty() || (m_database == column_def.schema()));
|
||||
|
||||
if (match)
|
||||
{
|
||||
// If the column matched, then we need to check whether the rule applies
|
||||
// to the user and host.
|
||||
|
||||
AccountMatcher matcher(zUser, zHost);
|
||||
|
||||
if (m_applies_to.size() != 0)
|
||||
{
|
||||
match = false;
|
||||
|
||||
vector<SAccount>::const_iterator i = std::find_if(m_applies_to.begin(),
|
||||
m_applies_to.end(),
|
||||
matcher);
|
||||
|
||||
match = (i != m_applies_to.end());
|
||||
}
|
||||
|
||||
if (match && (m_exempted.size() != 0))
|
||||
{
|
||||
// If it is still a match, we need to check whether the user/host is
|
||||
// exempted.
|
||||
|
||||
vector<SAccount>::const_iterator i = std::find_if(m_exempted.begin(),
|
||||
m_exempted.end(),
|
||||
matcher);
|
||||
|
||||
match = (i == m_exempted.end());
|
||||
}
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
void MaskingRules::Rule::rewrite(LEncString& s) const
|
||||
{
|
||||
bool rewritten = false;
|
||||
|
||||
size_t total_len = s.length();
|
||||
|
||||
if (!m_value.empty())
|
||||
{
|
||||
if (m_value.length() == total_len)
|
||||
{
|
||||
std::copy(m_value.begin(), m_value.end(), s.begin());
|
||||
rewritten = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!rewritten)
|
||||
{
|
||||
if (!m_fill.empty())
|
||||
{
|
||||
LEncString::iterator i = s.begin();
|
||||
size_t len = m_fill.length();
|
||||
|
||||
while (total_len)
|
||||
{
|
||||
if (total_len < len)
|
||||
{
|
||||
len = total_len;
|
||||
}
|
||||
|
||||
std::copy(m_fill.data(), m_fill.data() + len, i);
|
||||
|
||||
i += len;
|
||||
total_len -= len;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MXS_ERROR("Length of returned value \"%s\" is %u, while length of "
|
||||
"replacement value \"%s\" is %u, and no 'fill' value specified.",
|
||||
s.to_string().c_str(), (unsigned)s.length(),
|
||||
m_value.c_str(), (unsigned)m_value.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// MaskingRules
|
||||
//
|
||||
@ -702,3 +815,50 @@ std::auto_ptr<MaskingRules> MaskingRules::create_from(json_t* pRoot)
|
||||
|
||||
return sRules;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class RuleMatcher : std::unary_function<MaskingRules::SRule, bool>
|
||||
{
|
||||
public:
|
||||
RuleMatcher(const ComQueryResponse::ColumnDef& column_def,
|
||||
const char* zUser,
|
||||
const char* zHost)
|
||||
: m_column_def(column_def)
|
||||
, m_zUser(zUser)
|
||||
, m_zHost(zHost)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator()(const MaskingRules::SRule& sRule)
|
||||
{
|
||||
return sRule->matches(m_column_def, m_zUser, m_zHost);
|
||||
}
|
||||
|
||||
private:
|
||||
const ComQueryResponse::ColumnDef& m_column_def;
|
||||
const char* m_zUser;
|
||||
const char* m_zHost;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
const MaskingRules::Rule* MaskingRules::get_rule_for(const ComQueryResponse::ColumnDef& column_def,
|
||||
const char* zUser,
|
||||
const char* zHost) const
|
||||
{
|
||||
const Rule* pRule = NULL;
|
||||
|
||||
RuleMatcher matcher(column_def, zUser, zHost);
|
||||
vector<SRule>::const_iterator i = std::find_if(m_rules.begin(), m_rules.end(), matcher);
|
||||
|
||||
if (i != m_rules.end())
|
||||
{
|
||||
const SRule& sRule = *i;
|
||||
|
||||
pRule = sRule.get();
|
||||
}
|
||||
|
||||
return pRule;
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <jansson.h>
|
||||
#include "mysql.hh"
|
||||
|
||||
/**
|
||||
* @class MaskingRules
|
||||
@ -26,6 +27,8 @@
|
||||
*/
|
||||
class MaskingRules
|
||||
{
|
||||
friend class MaskingRulesTester;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @class Rule
|
||||
@ -103,6 +106,21 @@ public:
|
||||
*/
|
||||
static std::auto_ptr<Rule> create_from(json_t* pRule);
|
||||
|
||||
/**
|
||||
* Establish whether a rule matches a column definition and user/host.
|
||||
*
|
||||
* @param column_def A column definition.
|
||||
* @param zUser The current user.
|
||||
* @param zHost The current host.
|
||||
*
|
||||
* @return True, if the rule matches.
|
||||
*/
|
||||
bool matches(const ComQueryResponse::ColumnDef& column_def,
|
||||
const char* zUser,
|
||||
const char* zHost) const;
|
||||
|
||||
void rewrite(LEncString& s) const;
|
||||
|
||||
private:
|
||||
Rule(const Rule&);
|
||||
Rule& operator = (const Rule&);
|
||||
@ -147,11 +165,24 @@ public:
|
||||
*/
|
||||
static std::auto_ptr<MaskingRules> create_from(json_t* pRoot);
|
||||
|
||||
/**
|
||||
* Return the rule object that matches a column definition and user/host.
|
||||
*
|
||||
* @param column_def A column definition.
|
||||
* @param zUser The current user.
|
||||
* @param zHost The current host.
|
||||
*
|
||||
* @return A rule object that matches the column definition and user/host
|
||||
* or NULL if no such rule object exists.
|
||||
*
|
||||
* @attention The returned object remains value only as long as the
|
||||
* @c MaskingRules object remains valid.
|
||||
*/
|
||||
const Rule* get_rule_for(const ComQueryResponse::ColumnDef& column_def,
|
||||
const char* zUser,
|
||||
const char* zHost) const;
|
||||
|
||||
typedef std::tr1::shared_ptr<Rule> SRule;
|
||||
const std::vector<SRule>& rules() const
|
||||
{
|
||||
return m_rules;
|
||||
}
|
||||
|
||||
private:
|
||||
MaskingRules(json_t* pRoot, const std::vector<SRule>& rules);
|
||||
|
@ -11,6 +11,7 @@
|
||||
* Public License.
|
||||
*/
|
||||
|
||||
#define TESTING_MASKINGRULES
|
||||
#include "maskingrules.hh"
|
||||
#include <iostream>
|
||||
#include <maxscale/debug.h>
|
||||
@ -116,25 +117,6 @@ struct rule_test
|
||||
|
||||
const size_t nRule_tests = (sizeof(rule_tests) / sizeof(rule_tests[0]));
|
||||
|
||||
int test_parsing()
|
||||
{
|
||||
int rc = EXIT_SUCCESS;
|
||||
|
||||
for (size_t i = 0; i < nRule_tests; i++)
|
||||
{
|
||||
const rule_test& test = rule_tests[i];
|
||||
|
||||
auto_ptr<MaskingRules> sRules = MaskingRules::parse(test.zJson);
|
||||
|
||||
if ((sRules.get() && !test.valid) || (!sRules.get() && test.valid))
|
||||
{
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Valid, lot's of users.
|
||||
const char valid_users[] =
|
||||
"{"
|
||||
@ -190,49 +172,72 @@ struct expected_account
|
||||
|
||||
const size_t nExpected_accounts = (sizeof(expected_accounts)/sizeof(expected_accounts[0]));
|
||||
|
||||
int test_account_handling()
|
||||
class MaskingRulesTester
|
||||
{
|
||||
int rc = EXIT_SUCCESS;
|
||||
|
||||
auto_ptr<MaskingRules> sRules = MaskingRules::parse(valid_users);
|
||||
ss_dassert(sRules.get());
|
||||
|
||||
const vector<shared_ptr<MaskingRules::Rule> >& rules = sRules->rules();
|
||||
ss_dassert(rules.size() == 1);
|
||||
|
||||
shared_ptr<MaskingRules::Rule> sRule = rules[0];
|
||||
|
||||
const vector<shared_ptr<MaskingRules::Rule::Account> >& accounts = sRule->applies_to();
|
||||
ss_dassert(accounts.size() == nExpected_accounts);
|
||||
|
||||
int j = 0;
|
||||
for (vector<shared_ptr<MaskingRules::Rule::Account> >::const_iterator i = accounts.begin();
|
||||
i != accounts.end();
|
||||
++i)
|
||||
public:
|
||||
static int test_parsing()
|
||||
{
|
||||
const expected_account& account = expected_accounts[j];
|
||||
int rc = EXIT_SUCCESS;
|
||||
|
||||
string user = (*i)->user();
|
||||
|
||||
if (user != account.zUser)
|
||||
for (size_t i = 0; i < nRule_tests; i++)
|
||||
{
|
||||
cout << j << ": Expected \"" << account.zUser << "\", got \"" << user << "\"." << endl;
|
||||
rc = EXIT_FAILURE;
|
||||
const rule_test& test = rule_tests[i];
|
||||
|
||||
auto_ptr<MaskingRules> sRules = MaskingRules::parse(test.zJson);
|
||||
|
||||
if ((sRules.get() && !test.valid) || (!sRules.get() && test.valid))
|
||||
{
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
string host = (*i)->host();
|
||||
|
||||
if (host != account.zHost)
|
||||
{
|
||||
cout << j << ": Expected \"" << account.zHost << "\", got \"" << host << "\"." << endl;
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
++j;
|
||||
return rc;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
static int test_account_handling()
|
||||
{
|
||||
int rc = EXIT_SUCCESS;
|
||||
|
||||
auto_ptr<MaskingRules> sRules = MaskingRules::parse(valid_users);
|
||||
ss_dassert(sRules.get());
|
||||
|
||||
const vector<shared_ptr<MaskingRules::Rule> >& rules = sRules->m_rules;
|
||||
ss_dassert(rules.size() == 1);
|
||||
|
||||
shared_ptr<MaskingRules::Rule> sRule = rules[0];
|
||||
|
||||
const vector<shared_ptr<MaskingRules::Rule::Account> >& accounts = sRule->applies_to();
|
||||
ss_dassert(accounts.size() == nExpected_accounts);
|
||||
|
||||
int j = 0;
|
||||
for (vector<shared_ptr<MaskingRules::Rule::Account> >::const_iterator i = accounts.begin();
|
||||
i != accounts.end();
|
||||
++i)
|
||||
{
|
||||
const expected_account& account = expected_accounts[j];
|
||||
|
||||
string user = (*i)->user();
|
||||
|
||||
if (user != account.zUser)
|
||||
{
|
||||
cout << j << ": Expected \"" << account.zUser << "\", got \"" << user << "\"." << endl;
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
string host = (*i)->host();
|
||||
|
||||
if (host != account.zHost)
|
||||
{
|
||||
cout << j << ": Expected \"" << account.zHost << "\", got \"" << host << "\"." << endl;
|
||||
rc = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
++j;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
@ -240,8 +245,8 @@ int main()
|
||||
|
||||
if (mxs_log_init(NULL, ".", MXS_LOG_TARGET_DEFAULT))
|
||||
{
|
||||
rc = (test_parsing() == EXIT_FAILURE) ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
rc = (test_account_handling() == EXIT_FAILURE) ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
rc = (MaskingRulesTester::test_parsing() == EXIT_FAILURE) ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
rc = (MaskingRulesTester::test_account_handling() == EXIT_FAILURE) ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
Loading…
x
Reference in New Issue
Block a user