def validate_duration(self, attrs, source): value = attrs[source] try: expand_time(value) except ValueError: raise serializers.ValidationError("Invalid duration") return attrs
def add_block(self, cidr, who, source, why, duration=None, unblock_at=None, skip_whitelist=False, extend=True, autoscale=False): if duration: duration = expand_time(duration) now = timezone.now() if duration and not unblock_at: unblock_at = now + datetime.timedelta(seconds=duration) with advisory_lock("add_block") as acquired, transaction.atomic(): b = self.get_block(cidr) if b: if extend is False or b.unblock_at is None or (unblock_at and unblock_at <= b.unblock_at): logger.info('DUPE IP=%s', cidr) return b b.unblock_at = unblock_at BlockEntry.objects.filter(block_id=b.id).update(unblock_at=unblock_at) logger.info('EXTEND IP=%s time extended UNTIL=%s DURATION=%s', cidr, unblock_at, duration) b.save() return b if duration and autoscale: lb = self.get_last_block(cidr) if lb and lb.duration: last_duration = lb.duration and lb.duration.total_seconds() or duration scaled_duration = max(duration, self.scale_duration(lb.age.total_seconds(), last_duration)) logger.info("Scaled duration from %d to %d", duration, scaled_duration) duration = scaled_duration unblock_at = now + datetime.timedelta(seconds=duration) b = Block(cidr=cidr, who=who, source=source, why=why, added=now, unblock_at=unblock_at, skip_whitelist=skip_whitelist) b.save() # It is possible that a block is added, and then after it expires, # but before it is unblocked, a new block is added for that entry. # In that case, allow the new block # (since we don't know if a backend may have already unblocked the old one) # but set the old record as already unblocked. # This should prevent a "block,block,unblock" timeline that results in the address # ending up not actually blocked. pending_unblock_records = BlockEntry.objects.filter(removed__isnull=True, block__cidr=cidr).all() for e in pending_unblock_records: e.set_unblocked() e.save() quoted_why = quote(why.encode('ascii', 'ignore')) logger.info('BLOCK IP=%s WHO=%s SOURCE=%s WHY=%s UNTIL="%s" DURATION=%s', cidr, who, source, quoted_why, unblock_at, duration) return b
def test_expand_time(self): cases = [ ('10', 10), ('10s', 10), ('7m', 7 * 60), ('14m', 14 * 60), ('4h', 4 * 60 * 60), ('22h', 22 * 60 * 60), ('3d', 3 * 60 * 60 * 24), ('3mo', 3 * 60 * 60 * 24 * 30), ('2y', 2 * 60 * 60 * 24 * 365), ] for text, number in cases: self.assertEqual(expand_time(text), number)
def test_expand_time(self): cases = [ ('10', 10), ('10s', 10), ('7m', 7*60), ('14m', 14*60), ('4h', 4*60*60), ('22h', 22*60*60), ('3d', 3*60*60*24), ('3mo', 3*60*60*24*30), ('2y', 2*60*60*24*365), ] for text, number in cases: self.assertEqual(expand_time(text), number)
def add_block(self, cidr, who, source, why, duration=None, unblock_at=None, skip_whitelist=False, extend=True, autoscale=False): if duration: duration = expand_time(duration) now = timezone.now() if duration and not unblock_at: unblock_at = now + datetime.timedelta(seconds=duration) with advisory_lock("add_block") as acquired, transaction.atomic(): b = self.get_block(cidr) if b: if extend is False or b.unblock_at is None or (unblock_at and unblock_at <= b.unblock_at): logger.info('DUPE IP=%s', cidr) return b b.unblock_at = unblock_at BlockEntry.objects.filter(block_id=b.id).update(unblock_at=unblock_at) logger.info('EXTEND IP=%s time extended UNTIL=%s DURATION=%s', cidr, unblock_at, duration) b.save() return b if duration and autoscale: lb = self.get_last_block(cidr) if lb and lb.duration: last_duration = lb.duration and lb.duration.total_seconds() or duration scaled_duration = max(duration, self.scale_duration(lb.age.total_seconds(), last_duration)) logger.info("Scaled duration from %d to %d", duration, scaled_duration) duration = scaled_duration unblock_at = now + datetime.timedelta(seconds=duration) b = Block(cidr=cidr, who=who, source=source, why=why, added=now, unblock_at=unblock_at, skip_whitelist=skip_whitelist) b.save() #It is possible that a block is added, and then after it expires, but before it is unblocked, a new block is added for that entry. #In that case, allow the new block (since we don't know if a backend may have already unblocked the old one) #but set the old record as already unblocked. This should prevent a "block,block,unblock" timeline that results in the address #ending up not actually blocked. pending_unblock_records = BlockEntry.objects.filter(removed__isnull=True, block__cidr=cidr).all() for e in pending_unblock_records: e.set_unblocked() e.save() quoted_why = quote(why.encode('ascii', 'ignore')) logger.info('BLOCK IP=%s WHO=%s SOURCE=%s WHY=%s UNTIL="%s" DURATION=%s', cidr, who, source, quoted_why, unblock_at, duration) return b
def validate_duration(self, value): try: expand_time(value) except ValueError: raise serializers.ValidationError("Invalid duration") return value