From 5a195d9ce962ed12d7df0dd72fa2626c58d26287 Mon Sep 17 00:00:00 2001 From: Luciano Strika Date: Sun, 5 Aug 2018 20:50:18 -0300 Subject: [PATCH 1/6] abstract logic of ip_check to separate functions- --- lib/limits.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/lib/limits.py b/lib/limits.py index e10ad8a..8b283c6 100644 --- a/lib/limits.py +++ b/lib/limits.py @@ -21,6 +21,28 @@ from globals import log _WHITELIST = ['5.9.243.177'] + +def _time_caps(m, h, d): + return { + 'min': m, + 'hour': h, + 'day': d, + } + +def _log_visit(interval, ip): + if ip_address not in self.counter[interval]: + self.counter[interval][ip_address] = 0 + self.counter[interval][ip_address] += 1 + +def _limit_exceeded(interval, ip): + visits = self.counter[interval][ip_address] + limit = self.limit[interval] + return visits > limit + +def _report_excessive_visits(interval, ip): + log("%s LIMITED [%s for %s]" % (ip_address, self.limit[interval], interval)) + + class Limits(object): """ Queries limitation (by IP). From 9ac2ede034df06797ddbb691d36985256440ea67 Mon Sep 17 00:00:00 2001 From: Luciano Strika Date: Sun, 5 Aug 2018 20:50:49 -0300 Subject: [PATCH 2/6] create function that generates dictionaries of intervals and quantities --- lib/limits.py | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/lib/limits.py b/lib/limits.py index 8b283c6..98b7ea6 100644 --- a/lib/limits.py +++ b/lib/limits.py @@ -54,28 +54,21 @@ class Limits(object): def __init__(self): self.intervals = ['min', 'hour', 'day'] - self.divisor = { - 'min': 60, - 'hour': 3600, - 'day': 86400, - } + + self.divisor = _time_caps(60, 3600, 86400) + self.limit = _time_caps(30, 600, 1000) + self.last_update = _time_caps(0, 0, 0) + self.counter = { 'min': {}, 'hour': {}, 'day': {}, } - self.limit = { - 'min': 30, - 'hour': 600, - 'day': 1000, - } - self.last_update = { - 'min': 0, - 'hour': 0, - 'day': 0, - } + self._clear_counters_if_needed() + + def check_ip(self, ip_address): """ Check if `ip_address` is allowed, and if not raise an RuntimeError exception. @@ -85,11 +78,9 @@ class Limits(object): return None self._clear_counters_if_needed() for interval in self.intervals: - if ip_address not in self.counter[interval]: - self.counter[interval][ip_address] = 0 - self.counter[interval][ip_address] += 1 - if self.limit[interval] <= self.counter[interval][ip_address]: - log("%s LIMITED [%s for %s]" % (ip_address, self.limit[interval], interval)) + _log_visit(interval, ip) + if _limit_exceeded(interval, ip): + _report_excessive_visits(interval, ip) return ("Not so fast! Number of queries per %s is limited to %s" % (interval, self.limit[interval])) return None From 3a2cb573f64be32c86eca9c1b4d8dcb08f0fa883 Mon Sep 17 00:00:00 2001 From: Luciano Strika Date: Sun, 5 Aug 2018 20:52:15 -0300 Subject: [PATCH 3/6] correct typo in comment, and make wording a bit better --- lib/limits.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/limits.py b/lib/limits.py index 98b7ea6..019000a 100644 --- a/lib/limits.py +++ b/lib/limits.py @@ -1,12 +1,12 @@ """ -Connection limitatation. +Connection limitation. Number of connections from one IP is limited. -We have nothing against scripting and automated queries, -even in opposite, we encourage them, but there are some +We have nothing against scripting and automated queries. +Even the opposite, we encourage them. But there are some connection limits that even we can't handle. -Currently the limits set low, but they will be relaxed -in future. +Currently the limits are quite restrictive, but they will be relaxed +in the future. Usage: From cdb253b2a0a6a7419025877648c351114201b1cb Mon Sep 17 00:00:00 2001 From: Luciano Strika Date: Mon, 6 Aug 2018 18:26:04 -0300 Subject: [PATCH 4/6] refactor functions into methods --- lib/limits.py | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/limits.py b/lib/limits.py index 019000a..46ee975 100644 --- a/lib/limits.py +++ b/lib/limits.py @@ -29,18 +29,9 @@ def _time_caps(m, h, d): 'day': d, } -def _log_visit(interval, ip): - if ip_address not in self.counter[interval]: - self.counter[interval][ip_address] = 0 - self.counter[interval][ip_address] += 1 -def _limit_exceeded(interval, ip): - visits = self.counter[interval][ip_address] - limit = self.limit[interval] - return visits > limit -def _report_excessive_visits(interval, ip): - log("%s LIMITED [%s for %s]" % (ip_address, self.limit[interval], interval)) + class Limits(object): @@ -68,6 +59,21 @@ class Limits(object): self._clear_counters_if_needed() + def _log_visit(self, interval, ip): + if ip_address not in self.counter[interval]: + self.counter[interval][ip_address] = 0 + self.counter[interval][ip_address] += 1 + + def _limit_exceeded(self, interval, ip): + visits = self.counter[interval][ip_address] + limit = self.get_limit(interval) + return visits > limit + + def _get_limit(self, interval): + return self.limit[interval] + + def _report_excessive_visits(self, interval, ip): + log("%s LIMITED [%s for %s]" % (ip_address, self.get_limit(interval), interval)) def check_ip(self, ip_address): """ @@ -78,11 +84,11 @@ class Limits(object): return None self._clear_counters_if_needed() for interval in self.intervals: - _log_visit(interval, ip) - if _limit_exceeded(interval, ip): - _report_excessive_visits(interval, ip) + self._log_visit(interval, ip) + if self._limit_exceeded(interval, ip): + self._report_excessive_visits(interval, ip) return ("Not so fast! Number of queries per %s is limited to %s" - % (interval, self.limit[interval])) + % (interval, self.get_limit(interval) )) return None def reset(self): From 058cf454b5057a94354814e058575a4e56d8f339 Mon Sep 17 00:00:00 2001 From: Luciano Strika Date: Mon, 6 Aug 2018 18:26:18 -0300 Subject: [PATCH 5/6] replace / with // --- lib/limits.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/limits.py b/lib/limits.py index 46ee975..524430d 100644 --- a/lib/limits.py +++ b/lib/limits.py @@ -101,6 +101,6 @@ class Limits(object): def _clear_counters_if_needed(self): current_time = int(time.time()) for interval in self.intervals: - if current_time / self.divisor[interval] != self.last_update[interval]: + if current_time // self.divisor[interval] != self.last_update[interval]: self.counter[interval] = {} self.last_update[interval] = current_time / self.divisor[interval] From 5e82da17d716506309579c0865c817ac976df5b7 Mon Sep 17 00:00:00 2001 From: Luciano Strika Date: Mon, 6 Aug 2018 18:55:18 -0300 Subject: [PATCH 6/6] correct variable names --- lib/limits.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/limits.py b/lib/limits.py index 524430d..7e664b7 100644 --- a/lib/limits.py +++ b/lib/limits.py @@ -59,21 +59,21 @@ class Limits(object): self._clear_counters_if_needed() - def _log_visit(self, interval, ip): + def _log_visit(self, interval, ip_address): if ip_address not in self.counter[interval]: self.counter[interval][ip_address] = 0 self.counter[interval][ip_address] += 1 - def _limit_exceeded(self, interval, ip): + def _limit_exceeded(self, interval, ip_address): visits = self.counter[interval][ip_address] - limit = self.get_limit(interval) + limit = self._get_limit(interval) return visits > limit def _get_limit(self, interval): return self.limit[interval] - def _report_excessive_visits(self, interval, ip): - log("%s LIMITED [%s for %s]" % (ip_address, self.get_limit(interval), interval)) + def _report_excessive_visits(self, interval, ip_address): + log("%s LIMITED [%s for %s]" % (ip_address, self._get_limit(interval), interval)) def check_ip(self, ip_address): """ @@ -84,11 +84,11 @@ class Limits(object): return None self._clear_counters_if_needed() for interval in self.intervals: - self._log_visit(interval, ip) - if self._limit_exceeded(interval, ip): - self._report_excessive_visits(interval, ip) + self._log_visit(interval, ip_address) + if self._limit_exceeded(interval, ip_address): + self._report_excessive_visits(interval, ip_address) return ("Not so fast! Number of queries per %s is limited to %s" - % (interval, self.get_limit(interval) )) + % (interval, self._get_limit(interval) )) return None def reset(self):