normalization of DefinitionInitConfigReader (action / filter): client-side interpolation, etc.

pull/1669/head
sebres 2017-01-14 00:17:23 +01:00
parent de49f0c27f
commit 2ed2e7810d
6 changed files with 57 additions and 29 deletions

View File

@ -43,10 +43,15 @@ class ActionReader(DefinitionInitConfigReader):
"actionrepair": ["string", None], "actionrepair": ["string", None],
"actionban": ["string", None], "actionban": ["string", None],
"actionunban": ["string", None], "actionunban": ["string", None],
"norestored": ["string", None],
} }
def __init__(self, file_, jailName, initOpts, **kwargs): def __init__(self, file_, jailName, initOpts, **kwargs):
self._name = initOpts.get("actname", file_) actname = initOpts.get("actname")
if actname is None:
actname = file_
initOpts["actname"] = actname
self._name = actname
DefinitionInitConfigReader.__init__( DefinitionInitConfigReader.__init__(
self, file_, jailName, initOpts, **kwargs) self, file_, jailName, initOpts, **kwargs)
@ -64,16 +69,22 @@ class ActionReader(DefinitionInitConfigReader):
return self._name return self._name
def convert(self): def convert(self):
opts = self.getCombined(ignore=('timeout', 'bantime'))
# type-convert only after combined (otherwise boolean converting prevents substitution):
if opts.get('norestored'):
opts['norestored'] = self._convert_to_boolean(opts['norestored'])
# stream-convert:
head = ["set", self._jailName] head = ["set", self._jailName]
stream = list() stream = list()
stream.append(head + ["addaction", self._name]) stream.append(head + ["addaction", self._name])
multi = [] multi = []
for opt, optval in self._opts.iteritems(): for opt, optval in opts.iteritems():
if opt in self._configOpts: if opt in self._configOpts:
multi.append([opt, optval]) multi.append([opt, optval])
if self._initOpts: if self._initOpts:
for opt, optval in self._initOpts.iteritems(): for opt, optval in self._initOpts.iteritems():
multi.append([opt, optval]) if opt not in self._configOpts:
multi.append([opt, optval])
if len(multi) > 1: if len(multi) > 1:
stream.append(["multi-set", self._jailName, "action", self._name, multi]) stream.append(["multi-set", self._jailName, "action", self._name, multi])
elif len(multi): elif len(multi):

View File

@ -30,6 +30,7 @@ from ConfigParser import NoOptionError, NoSectionError
from .configparserinc import sys, SafeConfigParserWithIncludes, logLevel from .configparserinc import sys, SafeConfigParserWithIncludes, logLevel
from ..helpers import getLogger from ..helpers import getLogger
from ..server.action import CommandAction
# Gets the instance of the logger. # Gets the instance of the logger.
logSys = getLogger(__name__) logSys = getLogger(__name__)
@ -320,5 +321,27 @@ class DefinitionInitConfigReader(ConfigReader):
if not opt in self._initOpts: if not opt in self._initOpts:
self._initOpts[opt] = v self._initOpts[opt] = v
def _convert_to_boolean(self, value):
return value.lower() in ("1", "yes", "true", "on")
def getCombined(self, ignore=()):
combinedopts = self._opts
ignore = set(ignore).copy()
if self._initOpts:
combinedopts = _merge_dicts(self._opts, self._initOpts)
if not len(combinedopts):
return {}
# ignore conditional options:
for n in combinedopts:
cond = SafeConfigParserWithIncludes.CONDITIONAL_RE.match(n)
if cond:
n, cond = cond.groups()
ignore.add(n)
# substiture options already specified direct:
opts = CommandAction.substituteRecursiveTags(combinedopts, ignore=ignore)
if not opts:
raise ValueError('recursive tag definitions unable to be resolved')
return opts
def convert(self): def convert(self):
raise NotImplementedError raise NotImplementedError

View File

@ -27,8 +27,7 @@ __license__ = "GPL"
import os import os
import shlex import shlex
from .configreader import DefinitionInitConfigReader, _merge_dicts from .configreader import DefinitionInitConfigReader
from ..server.action import CommandAction
from ..helpers import getLogger from ..helpers import getLogger
# Gets the instance of the logger. # Gets the instance of the logger.
@ -52,17 +51,6 @@ class FilterReader(DefinitionInitConfigReader):
def getFile(self): def getFile(self):
return self.__file return self.__file
def getCombined(self):
combinedopts = self._opts
if self._initOpts:
combinedopts = _merge_dicts(self._opts, self._initOpts)
if not len(combinedopts):
return {}
opts = CommandAction.substituteRecursiveTags(combinedopts)
if not opts:
raise ValueError('recursive tag definitions unable to be resolved')
return opts
def convert(self): def convert(self):
stream = list() stream = list()
opts = self.getCombined() opts = self.getCombined()
@ -70,6 +58,7 @@ class FilterReader(DefinitionInitConfigReader):
return stream return stream
for opt, value in opts.iteritems(): for opt, value in opts.iteritems():
if opt in ("failregex", "ignoreregex"): if opt in ("failregex", "ignoreregex"):
if value is None: continue
multi = [] multi = []
for regex in value.split('\n'): for regex in value.split('\n'):
# Do not send a command if the rule is empty. # Do not send a command if the rule is empty.
@ -87,6 +76,7 @@ class FilterReader(DefinitionInitConfigReader):
stream.append(["set", self._jailName, "datepattern", value]) stream.append(["set", self._jailName, "datepattern", value])
# Do not send a command if the match is empty. # Do not send a command if the match is empty.
elif opt == 'journalmatch': elif opt == 'journalmatch':
if value is None: continue
for match in value.split("\n"): for match in value.split("\n"):
if match == '': continue if match == '': continue
stream.append( stream.append(

View File

@ -136,7 +136,8 @@ class JailReader(ConfigReader):
if not filterName: if not filterName:
raise JailDefError("Invalid filter definition %r" % flt) raise JailDefError("Invalid filter definition %r" % flt)
self.__filter = FilterReader( self.__filter = FilterReader(
filterName, self.__name, filterOpt, share_config=self.share_config, basedir=self.getBaseDir()) filterName, self.__name, filterOpt,
share_config=self.share_config, basedir=self.getBaseDir())
ret = self.__filter.read() ret = self.__filter.read()
# merge options from filter as 'known/...': # merge options from filter as 'known/...':
self.__filter.getOptions(self.__opts) self.__filter.getOptions(self.__opts)

View File

@ -365,7 +365,7 @@ class CommandAction(ActionBase):
return self._executeOperation('<actionreload>', 'reloading') return self._executeOperation('<actionreload>', 'reloading')
@classmethod @classmethod
def substituteRecursiveTags(cls, inptags, conditional=''): def substituteRecursiveTags(cls, inptags, conditional='', ignore=()):
"""Sort out tag definitions within other tags. """Sort out tag definitions within other tags.
Since v.0.9.2 supports embedded interpolation (see test cases for examples). Since v.0.9.2 supports embedded interpolation (see test cases for examples).
@ -387,21 +387,26 @@ class CommandAction(ActionBase):
# copy return tags dict to prevent modifying of inptags: # copy return tags dict to prevent modifying of inptags:
tags = inptags.copy() tags = inptags.copy()
t = TAG_CRE t = TAG_CRE
ignore = set(ignore)
done = cls._escapedTags.copy() | ignore
# repeat substitution while embedded-recursive (repFlag is True) # repeat substitution while embedded-recursive (repFlag is True)
done = cls._escapedTags.copy()
while True: while True:
repFlag = False repFlag = False
# substitute each value: # substitute each value:
for tag in tags.iterkeys(): for tag in tags.iterkeys():
# ignore escaped or already done: # ignore escaped or already done (or in ignore list):
if tag in done: continue if tag in done: continue
value = str(tags[tag]) value = orgval = str(tags[tag])
# search and replace all tags within value, that can be interpolated using other tags: # search and replace all tags within value, that can be interpolated using other tags:
m = t.search(value) m = t.search(value)
refCounts = {} refCounts = {}
#logSys.log(5, 'TAG: %s, value: %s' % (tag, value)) #logSys.log(5, 'TAG: %s, value: %s' % (tag, value))
while m: while m:
found_tag = m.group(1) found_tag = m.group(1)
# don't replace tags that should be currently ignored (pre-replacement):
if found_tag in ignore:
m = t.search(value, m.end())
continue
#logSys.log(5, 'found: %s' % found_tag) #logSys.log(5, 'found: %s' % found_tag)
if found_tag == tag or refCounts.get(found_tag, 1) > MAX_TAG_REPLACE_COUNT: if found_tag == tag or refCounts.get(found_tag, 1) > MAX_TAG_REPLACE_COUNT:
# recursive definitions are bad # recursive definitions are bad
@ -429,7 +434,7 @@ class CommandAction(ActionBase):
m = t.search(value, m.start()) m = t.search(value, m.start())
#logSys.log(5, 'TAG: %s, newvalue: %s' % (tag, value)) #logSys.log(5, 'TAG: %s, newvalue: %s' % (tag, value))
# was substituted? # was substituted?
if tags[tag] != value: if orgval != value:
# check still contains any tag - should be repeated (possible embedded-recursive substitution): # check still contains any tag - should be repeated (possible embedded-recursive substitution):
if t.search(value): if t.search(value):
repFlag = True repFlag = True

View File

@ -347,7 +347,7 @@ class FilterReaderTest(unittest.TestCase):
['set', 'testcase01', 'addjournalmatch', ['set', 'testcase01', 'addjournalmatch',
"FIELD= with spaces ", "+", "AFIELD= with + char and spaces"], "FIELD= with spaces ", "+", "AFIELD= with + char and spaces"],
['set', 'testcase01', 'datepattern', "%Y %m %d %H:%M:%S"], ['set', 'testcase01', 'datepattern', "%Y %m %d %H:%M:%S"],
['set', 'testcase01', 'maxlines', "1"], # Last for overide test ['set', 'testcase01', 'maxlines', 1], # Last for overide test
] ]
filterReader = FilterReader("testcase01", "testcase01", {}) filterReader = FilterReader("testcase01", "testcase01", {})
filterReader.setBaseDir(TEST_FILES_DIR) filterReader.setBaseDir(TEST_FILES_DIR)
@ -517,12 +517,10 @@ class JailsReaderTest(LogCaptureTestCase):
['add', 'brokenaction', 'auto'], ['add', 'brokenaction', 'auto'],
['set', 'brokenaction', 'addfailregex', '<IP>'], ['set', 'brokenaction', 'addfailregex', '<IP>'],
['set', 'brokenaction', 'addaction', 'brokenaction'], ['set', 'brokenaction', 'addaction', 'brokenaction'],
['set', ['multi-set', 'brokenaction', 'action', 'brokenaction', [
'brokenaction', ['actionban', 'hit with big stick <ip>'],
'action', ['actname', 'brokenaction']
'brokenaction', ]],
'actionban',
'hit with big stick <ip>'],
['add', 'parse_to_end_of_jail.conf', 'auto'], ['add', 'parse_to_end_of_jail.conf', 'auto'],
['set', 'parse_to_end_of_jail.conf', 'addfailregex', '<IP>'], ['set', 'parse_to_end_of_jail.conf', 'addfailregex', '<IP>'],
['start', 'emptyaction'], ['start', 'emptyaction'],