MXS-1346: Move rule matching into the User class

The User class now only exposes the `match` method which can be used to
check if any of the rules for a user match a query. Further cleanup is
required once individual rule classes have been implemented.
This commit is contained in:
Markus Mäkelä
2017-08-31 18:49:14 +03:00
parent f7b978b2a2
commit c55c46ac0c
4 changed files with 164 additions and 150 deletions

View File

@ -1917,146 +1917,6 @@ queryresolved:
return matches; return matches;
} }
/**
* Check if the query matches any of the rules in the user's rules.
* @param my_instance Fwfilter instance
* @param my_session Fwfilter session
* @param queue The GWBUF containing the query
* @param user The user whose rules are checked
* @return True if the query matches at least one of the rules otherwise false
*/
bool check_match_any(FW_INSTANCE* my_instance, FW_SESSION* my_session,
GWBUF *queue, SUser user, char** rulename)
{
bool rval = false;
if (!user->rules_or.empty() &&
(modutil_is_SQL(queue) || modutil_is_SQL_prepare(queue) ||
MYSQL_IS_COM_INIT_DB((uint8_t*)GWBUF_DATA(queue))))
{
char *fullquery = modutil_get_SQL(queue);
if (fullquery)
{
for (RuleList::iterator it = user->rules_or.begin(); it != user->rules_or.end(); it++)
{
if (rule_is_active(*it))
{
if (rule_matches(my_instance, my_session, queue, *it, fullquery))
{
*rulename = MXS_STRDUP_A((*it)->name.c_str());
rval = true;
break;
}
}
}
MXS_FREE(fullquery);
}
}
return rval;
}
/**
* Append and possibly reallocate string
* @param dest Destination where the string is appended or NULL if nothing has
* been allocated yet
* @param size Size of @c dest
* @param src String to append to @c dest
*/
void append_string(char** dest, size_t* size, const char* src)
{
if (*dest == NULL)
{
*dest = MXS_STRDUP_A(src);
*size = strlen(src);
}
else
{
if (*size < strlen(*dest) + strlen(src) + 3)
{
size_t newsize = strlen(*dest) + strlen(src) + 3;
char* tmp = (char*)MXS_REALLOC(*dest, newsize);
if (tmp)
{
*size = newsize;
*dest = tmp;
}
else
{
return;
}
}
strcat(*dest, ", ");
strcat(*dest, src);
}
}
/**
* Check if the query matches all rules in the user's rules
*
* @param my_instance Filter instance
* @param my_session Filter session
* @param queue Buffer containing the query
* @param user The user whose rules are checked
* @param strict_all Whether the first match stops the processing
* @param rulename Pointer where error messages are stored
*
* @return True if the query matches all of the rules otherwise false
*/
bool check_match_all(FW_INSTANCE* my_instance, FW_SESSION* my_session,
GWBUF *queue, SUser user, bool strict_all, char** rulename)
{
bool rval = false;
bool have_active_rule = false;
RuleList& rules = strict_all ? user->rules_strict_and : user->rules_and;
char *matched_rules = NULL;
size_t size = 0;
if (!rules.empty() && (modutil_is_SQL(queue) || modutil_is_SQL_prepare(queue)))
{
char *fullquery = modutil_get_SQL(queue);
if (fullquery)
{
rval = true;
for (RuleList::iterator it = rules.begin(); it != rules.end(); it++)
{
if (!rule_is_active(*it))
{
have_active_rule = true;
if (rule_matches(my_instance, my_session, queue, *it, fullquery))
{
append_string(&matched_rules, &size, (*it)->name.c_str());
}
else
{
rval = false;
if (strict_all)
{
break;
}
}
}
}
if (!have_active_rule)
{
/** No active rules */
rval = false;
}
MXS_FREE(fullquery);
}
}
/** Set the list of matched rule names */
*rulename = matched_rules;
return rval;
}
/** /**
* Retrieve the user specific data for this session * Retrieve the user specific data for this session
* *
@ -2178,15 +2038,8 @@ routeQuery(MXS_FILTER *instance, MXS_FILTER_SESSION *session, GWBUF *queue)
if (user) if (user)
{ {
bool match = false;
char* rname = NULL; char* rname = NULL;
bool match = user->match(my_instance, my_session, analyzed_queue, &rname);
if (check_match_any(my_instance, my_session, analyzed_queue, user, &rname) ||
check_match_all(my_instance, my_session, analyzed_queue, user, false, &rname) ||
check_match_all(my_instance, my_session, analyzed_queue, user, true, &rname))
{
match = true;
}
switch (my_instance->action) switch (my_instance->action)
{ {

View File

@ -199,5 +199,16 @@ typedef struct
/** Typedef for a list of strings */ /** Typedef for a list of strings */
typedef std::list<std::string> ValueList; typedef std::list<std::string> ValueList;
/** Temporary typedef for SRule */
class Rule;
typedef std::tr1::shared_ptr<Rule> SRule;
/** Helper function for strdup'ing in printf style */ /** Helper function for strdup'ing in printf style */
char* create_error(const char* format, ...); char* create_error(const char* format, ...);
/**
* Check if a rule matches
*/
bool rule_matches(FW_INSTANCE* my_instance, FW_SESSION* my_session,
GWBUF *queue, SRule rule, char* query);
bool rule_is_active(SRule rule);

View File

@ -13,6 +13,10 @@
#include "users.hh" #include "users.hh"
#include <maxscale/alloc.h>
#include <maxscale/modutil.h>
#include <maxscale/protocol/mysql.h>
User::User(std::string name): User::User(std::string name):
m_name(name) m_name(name)
{ {
@ -48,3 +52,123 @@ void User::append_rules(match_type mode, const RuleList& rules)
break; break;
} }
} }
static bool should_match(GWBUF* buffer)
{
return modutil_is_SQL(buffer) || modutil_is_SQL_prepare(buffer) ||
MYSQL_IS_COM_INIT_DB(GWBUF_DATA(buffer));
}
/**
* Check if the query matches any of the rules in the user's rules.
* @param my_instance Fwfilter instance
* @param my_session Fwfilter session
* @param queue The GWBUF containing the query
* @param user The user whose rules are checked
* @return True if the query matches at least one of the rules otherwise false
*/
bool User::match_any(FW_INSTANCE* my_instance, FW_SESSION* my_session,
GWBUF *queue, char** rulename)
{
bool rval = false;
if (rules_or.size() > 0 && should_match(queue))
{
char *fullquery = modutil_get_SQL(queue);
if (fullquery)
{
for (RuleList::iterator it = rules_or.begin(); it != rules_or.end(); it++)
{
if (rule_is_active(*it))
{
if (rule_matches(my_instance, my_session, queue, *it, fullquery))
{
*rulename = MXS_STRDUP_A((*it)->name.c_str());
rval = true;
break;
}
}
}
MXS_FREE(fullquery);
}
}
return rval;
}
/**
* Check if the query matches all rules in the user's rules
*
* @param my_instance Filter instance
* @param my_session Filter session
* @param queue Buffer containing the query
* @param user The user whose rules are checked
* @param strict_all Whether the first match stops the processing
* @param rulename Pointer where error messages are stored
*
* @return True if the query matches all of the rules otherwise false
*/
bool User::do_match(FW_INSTANCE* my_instance, FW_SESSION* my_session,
GWBUF *queue, match_mode mode, char** rulename)
{
bool rval = false;
bool have_active_rule = false;
std::string matching_rules;
RuleList& rules = mode == User::ALL ? rules_and : rules_strict_and;
if (rules.size() > 0 && should_match(queue))
{
char *fullquery = modutil_get_SQL(queue);
if (fullquery)
{
rval = true;
for (RuleList::iterator it = rules.begin(); it != rules.end(); it++)
{
if (!rule_is_active(*it))
{
have_active_rule = true;
if (rule_matches(my_instance, my_session, queue, *it, fullquery))
{
matching_rules += (*it)->name;
matching_rules += " ";
}
else
{
rval = false;
if (mode == User::STRICT)
{
break;
}
}
}
}
if (!have_active_rule)
{
/** No active rules */
rval = false;
}
MXS_FREE(fullquery);
}
}
/** Set the list of matched rule names */
if (matching_rules.length() > 0)
{
*rulename = MXS_STRDUP_A(matching_rules.c_str());
}
return rval;
}
bool User::match(FW_INSTANCE* instance, FW_SESSION* session, GWBUF* buffer, char** rulename)
{
return match_any(instance, session, buffer, rulename) ||
do_match(instance, session, buffer, User::ALL, rulename) ||
do_match(instance, session, buffer, User::STRICT, rulename);
}

View File

@ -57,13 +57,39 @@ public:
*/ */
void append_rules(match_type mode, const RuleList& rules); void append_rules(match_type mode, const RuleList& rules);
/**
* Check if a query matches some rule
*
* @param instance Filter instance
* @param session Filter session
* @param buffer Buffer containing the query
* @param rulename Names of rules that this query matched
*
* @return True if query matches
*/
bool match(FW_INSTANCE* instance, FW_SESSION* session, GWBUF* buffer, char** rulename);
private:
enum match_mode
{
ALL,
STRICT
};
RuleList rules_or; /*< If any of these rules match the action is triggered */ RuleList rules_or; /*< If any of these rules match the action is triggered */
RuleList rules_and; /*< All of these rules must match for the action to trigger */ RuleList rules_and; /*< All of these rules must match for the action to trigger */
RuleList rules_strict_and; /*< rules that skip the rest of the rules if one of them RuleList rules_strict_and; /*< rules that skip the rest of the rules if one of them
* fails. This is only for rules paired with 'match strict_all'. */ * fails. This is only for rules paired with 'match strict_all'. */
std::string m_name; /*< Name of the user */
private: /**
std::string m_name; /*< Name of the user */ * Functions for matching rules
*/
bool match_any(FW_INSTANCE* my_instance, FW_SESSION* my_session,
GWBUF *queue, char** rulename);
bool do_match(FW_INSTANCE* my_instance, FW_SESSION* my_session,
GWBUF *queue, match_mode mode, char** rulename);
}; };
typedef std::tr1::shared_ptr<User> SUser; typedef std::tr1::shared_ptr<User> SUser;