From 79350e916e79ec2d91d3430e6796f26da386ecf0 Mon Sep 17 00:00:00 2001 From: sebres Date: Fri, 29 Jun 2018 18:33:24 +0200 Subject: [PATCH] fixes issue with wrong-chars in string items of CallingMap by its representation, additionally don't calculate values implicitly (may be unexpected for some constellations resp. too slow in DEBUG) --- fail2ban/server/action.py | 31 ++++++++++++++++++++++++------- fail2ban/server/actions.py | 6 +++++- fail2ban/tests/actiontestcase.py | 9 +++++++-- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/fail2ban/server/action.py b/fail2ban/server/action.py index 699d95eb..fcc10e96 100644 --- a/fail2ban/server/action.py +++ b/fail2ban/server/action.py @@ -83,6 +83,8 @@ class CallingMap(MutableMapping, object): The dictionary data which can be accessed to obtain items uncalled """ + CM_REPR_ITEMS = () + # immutable=True saves content between actions, without interim copying (save original on demand, recoverable via reset) __slots__ = ('data', 'storage', 'immutable', '__org_data') def __init__(self, *args, **kwargs): @@ -98,14 +100,29 @@ class CallingMap(MutableMapping, object): pass self.immutable = immutable - def __repr__(self): - return "%s(%r)" % (self.__class__.__name__, self._asdict()) + def _asrepr(self, calculated=False): + # be sure it is suitable as string, so use str as checker: + return "%s(%r)" % (self.__class__.__name__, self._asdict(calculated, str)) - def _asdict(self): - try: - return dict(self) - except: - return dict(self.data, **self.storage) + __repr__ = _asrepr + + def _asdict(self, calculated=False, checker=None): + d = dict(self.data, **self.storage) + if not calculated: + return dict((n,v) for n,v in d.iteritems() \ + if not callable(v) or n in self.CM_REPR_ITEMS) + for n,v in d.items(): + if callable(v): + try: + # calculate: + v = self.__getitem__(n) + # convert if needed: + if checker: checker(v) + # store calculated: + d[n] = v + except: # can't calculate - just ignore it + pass + return d def getRawItem(self, key): try: diff --git a/fail2ban/server/actions.py b/fail2ban/server/actions.py index d90fca03..f7cb6ce6 100644 --- a/fail2ban/server/actions.py +++ b/fail2ban/server/actions.py @@ -290,6 +290,8 @@ class Actions(JailThread, Mapping): class ActionInfo(CallingMap): + CM_REPR_ITEMS = ("fid", "raw-ticket") + AI_DICT = { "ip": lambda self: self.__ticket.getIP(), "family": lambda self: self['ip'].familyStr, @@ -307,7 +309,9 @@ class Actions(JailThread, Mapping): "ipmatches": lambda self: "\n".join(self._mi4ip(True).getMatches()), "ipjailmatches": lambda self: "\n".join(self._mi4ip().getMatches()), "ipfailures": lambda self: self._mi4ip(True).getAttempt(), - "ipjailfailures": lambda self: self._mi4ip().getAttempt() + "ipjailfailures": lambda self: self._mi4ip().getAttempt(), + # raw ticket info: + "raw-ticket": lambda self: repr(self.__ticket) } __slots__ = CallingMap.__slots__ + ('__ticket', '__jail', '__mi4ip') diff --git a/fail2ban/tests/actiontestcase.py b/fail2ban/tests/actiontestcase.py index d05d4562..6df54e13 100644 --- a/fail2ban/tests/actiontestcase.py +++ b/fail2ban/tests/actiontestcase.py @@ -567,13 +567,18 @@ class CommandActionTest(LogCaptureTestCase): 'b': lambda self: self['a'] + 6, 'c': '' }) - s = repr(m) + s = repr(m); # only stored values (no calculated) + self.assertNotIn("'a': ", s) + self.assertNotIn("'b': ", s) + self.assertIn("'c': ''", s) + + s = m._asrepr(True) # all values (including calculated) self.assertIn("'a': 5", s) self.assertIn("'b': 11", s) self.assertIn("'c': ''", s) m['c'] = lambda self: self['xxx'] + 7; # unresolvable - s = repr(m) + s = m._asrepr(True) self.assertIn("'a': 5", s) self.assertIn("'b': 11", s) self.assertIn("'c': ", s) # presents as callable