diff --git a/docs/CONFIGURATION.rst b/docs/CONFIGURATION.rst index a169a3e..d670388 100644 --- a/docs/CONFIGURATION.rst +++ b/docs/CONFIGURATION.rst @@ -187,6 +187,7 @@ for IPv4, ``/48`` for IPv6). enabled = true ratelimit = 2 direct-reply = 3 + reply-to-self = false enabled enable guard, recommended in production. Not useful for debugging @@ -199,6 +200,11 @@ direct-reply how many comments directly to the thread (prevent a simple `while true; do curl ...; done`. +reply-to-self + allow commenters to reply to their own comments when they could still edit + the comment. After the editing timeframe is gone, commenters can reply to + their own comments anyways. + Appendum --------- diff --git a/isso/core.py b/isso/core.py index d7ec371..061dfee 100644 --- a/isso/core.py +++ b/isso/core.py @@ -120,7 +120,8 @@ class Config: "[guard]", "enabled = true", "ratelimit = 2", - "direct-reply = 3" + "direct-reply = 3", + "reply-to-self = false" ] @classmethod diff --git a/isso/db/spam.py b/isso/db/spam.py index bed2014..b86361b 100644 --- a/isso/db/spam.py +++ b/isso/db/spam.py @@ -10,6 +10,7 @@ class Guard: self.db = db self.conf = db.conf.section("guard") + self.max_age = db.conf.getint("general", "max-age") def validate(self, uri, comment): @@ -49,6 +50,18 @@ class Guard: if len(rv) >= self.conf.getint("direct-reply"): return False, "%i direct responses to %s" % (len(rv), uri) + elif self.conf.getboolean("reply-to-self") == False: + rv = self.db.execute([ + 'SELECT id FROM comments WHERE' + ' remote_addr = ?', + 'AND id = ?', + 'AND ? - created < ?' + ], (comment["remote_addr"], comment["parent"], + time.time(), self.max_age)).fetchall() + + if len(rv) > 0: + return False, "edit time frame is still open" + return True, "" def _spam(self, uri, comment): diff --git a/specs/test_guard.py b/specs/test_guard.py index e759f26..f1ed61c 100644 --- a/specs/test_guard.py +++ b/specs/test_guard.py @@ -23,13 +23,14 @@ class TestGuard(unittest.TestCase): def setUp(self): self.path = tempfile.NamedTemporaryFile().name - def makeClient(self, ip, ratelimit=2, direct_reply=3): + def makeClient(self, ip, ratelimit=2, direct_reply=3, self_reply=False): conf = core.Config.load(None) conf.set("general", "dbpath", self.path) conf.set("guard", "enabled", "true") conf.set("guard", "ratelimit", str(ratelimit)) conf.set("guard", "direct-reply", str(direct_reply)) + conf.set("guard", "reply-to-self", "1" if self_reply else "0") class App(Isso, core.Mixin): pass @@ -78,3 +79,24 @@ class TestGuard(unittest.TestCase): assert rv.status_code == 403 assert "direct responses to" in rv.data + + def testSelfReply(self): + + payload = lambda id: json.dumps({"text": "...", "parent": id}) + + client = self.makeClient("127.0.0.1", self_reply=False) + assert client.post("/new?uri=test", data=self.data).status_code == 201 + assert client.post("/new?uri=test", data=payload(1)).status_code == 403 + + client.application.db.execute([ + "UPDATE comments SET", + " created = created - ?", + "WHERE id = 1" + ], (client.application.conf.getint("general", "max-age"), )) + + assert client.post("/new?uri=test", data=payload(1)).status_code == 201 + + client = self.makeClient("128.0.0.1", ratelimit=3, self_reply=False) + assert client.post("/new?uri=test", data=self.data).status_code == 201 + assert client.post("/new?uri=test", data=payload(1)).status_code == 201 + assert client.post("/new?uri=test", data=payload(2)).status_code == 201