コード例 #1
0
def make_reset_token(token_cls, user, issue_limit=3):
    """Generate a password reset token or account recovery token.

    Checks a ratelimit to ensure that the token isn't being reset too often.
    There is also a global check on resets, such that more than 1000 per hour
    will trigger obvious and loud breakage via a ValueError.

    Issuing (or failing to issue) a token are added to user's notes.
    """

    # check if we've hit the ratelimit
    reset_count_key = "token:%s_count:%s" % (token_cls.__name__, user._id)
    time_slice = ratelimit.get_timeslice(int(token_cls._ttl))
    usage = ratelimit.record_usage(reset_count_key, time_slice)
    if usage > issue_limit:
        user.add_note("Exceeded password/email reset max attempts.")
        return None

    # check if we've hit the global rate limit and fail badly
    reset_count_global = "token:%s_count_global" % (token_cls.__name__, )
    global_time_slice = ratelimit.get_timeslice(60 * 60)
    global_usage = ratelimit.record_usage(reset_count_global,
                                          global_time_slice)

    if global_usage > 1000:
        raise ValueError(
            "Somebody's beating the hell out of the password reset endpoint")

    # all is well.  issue the token.
    token = token_cls._new(user)

    return token
コード例 #2
0
ファイル: token.py プロジェクト: zeantsoi/reddit
def make_reset_token(token_cls, user, issue_limit=3):
    """Generate a password reset token or account recovery token.

    Checks a ratelimit to ensure that the token isn't being reset too often.
    There is also a global check on resets, such that more than 1000 per hour
    will trigger obvious and loud breakage via a ValueError.

    Issuing (or failing to issue) a token are added to user's notes.
    """

    # check if we've hit the ratelimit
    reset_count_key = "token:%s_count:%s" % (token_cls.__name__, user._id)
    time_slice = ratelimit.get_timeslice(int(token_cls._ttl))
    usage = ratelimit.record_usage(reset_count_key, time_slice)
    if usage > issue_limit:
        user.add_note("Exceeded password/email reset max attempts.")
        return None

    # check if we've hit the global rate limit and fail badly
    reset_count_global = "token:%s_count_global" % (token_cls.__name__,)
    global_time_slice = ratelimit.get_timeslice(60 * 60)
    global_usage = ratelimit.record_usage(reset_count_global,
                                          global_time_slice)

    if global_usage > 1000:
        raise ValueError(
            "Somebody's beating the hell out of the password reset endpoint"
        )

    # all is well.  issue the token.
    token = token_cls._new(user)

    return token
コード例 #3
0
ファイル: ratelimit_test.py プロジェクト: zeantsoi/reddit
    def test_record_usage_multi(self):
        self.now = 24 * 3600 + 5
        tsa = ratelimit.get_timeslice(3600)
        tsb = ratelimit.get_timeslice(700)
        ratelimit.record_usage_multi([('a', tsa), ('b', tsb)])
        self.assertEquals(1, self.cache['rl:a-000000'])
        self.assertEquals(1, self.cache['rl:b-235500'])

        ratelimit.record_usage_multi([('a', tsa), ('b', tsb)])
        self.assertEquals(2, self.cache['rl:a-000000'])
        self.assertEquals(2, self.cache['rl:b-235500'])
コード例 #4
0
ファイル: ratelimit_test.py プロジェクト: zeantsoi/reddit
    def test_record_usage(self):
        self.now = 24 * 3600 + 5
        ts = ratelimit.get_timeslice(3600)
        ratelimit.record_usage('a', ts)
        self.assertEquals(1, self.cache['rl:a-000000'])
        ratelimit.record_usage('a', ts)
        self.assertEquals(2, self.cache['rl:a-000000'])

        self.now = 24 * 3600 + 5 * 3600
        ts = ratelimit.get_timeslice(3600)
        ratelimit.record_usage('a', ts)
        self.assertEquals(1, self.cache['rl:a-050000'])
コード例 #5
0
ファイル: token.py プロジェクト: zeantsoi/reddit
 def ratelimit_expired(self, limit=3):
     key = "ratelimit:%s:%s" % (self.__class__.__name__, self.user_id)
     time_slice = ratelimit.get_timeslice(60 * 60)
     usage = ratelimit.record_usage(key, time_slice)
     if usage > limit:
         self.consume()
         return True
コード例 #6
0
 def ratelimit_expired(self, limit=3):
     key = "ratelimit:%s:%s" % (self.__class__.__name__, self.user_id)
     time_slice = ratelimit.get_timeslice(60 * 60)
     usage = ratelimit.record_usage(key, time_slice)
     if usage > limit:
         self.consume()
         return True
コード例 #7
0
ファイル: ratelimit_test.py プロジェクト: zeantsoi/reddit
    def test_record_usage_multi_across_slice_expiration(self):
        self.now = 24 * 3600 + 5
        tsa = ratelimit.get_timeslice(3600)
        tsb = ratelimit.get_timeslice(700)
        real_incr = self.cache.incr
        evicted = False

        def fake_incr_multi(keys, delta=1):
            if evicted:
                for key in keys:
                    del self.cache[key]
                raise pylibmc.NotFound()
            return real_incr(key)

        with patch.object(self.cache, 'incr_multi', fake_incr_multi):
            # Forcibly evict the key before incr is called, but after the
            # initial add call inside record_usage.
            evicted = True
            ratelimit.record_usage_multi([('a', tsa), ('b', tsb)])
            self.assertEquals(1, self.cache['rl:a-000000'])
            self.assertEquals(1, self.cache['rl:b-235500'])
コード例 #8
0
ファイル: ratelimit_test.py プロジェクト: AppleBetas/reddit
    def test_record_usage_across_slice_expiration(self):
        self.now = 24 * 3600 + 5
        ts = ratelimit.get_timeslice(3600)
        real_incr = self.cache.incr
        evicted = False

        def fake_incr(key):
            if evicted:
                del self.cache[key]
                raise pylibmc.NotFound()
            return real_incr(key)

        with patch.object(self.cache, 'incr', fake_incr):
            # Forcibly evict the key before incr() is called, but after the
            # initial add() call inside record_usage().
            evicted = True
            ratelimit.record_usage('a', ts)
            self.assertEquals(1, self.cache['a-000000'])
コード例 #9
0
ファイル: ratelimit_test.py プロジェクト: z0r0/saidit
    def test_record_usage_across_slice_expiration(self):
        self.now = 24 * 3600 + 5
        ts = ratelimit.get_timeslice(3600)
        real_incr = self.cache.incr
        evicted = False

        def fake_incr(key):
            if evicted:
                del self.cache[key]
                raise pylibmc.NotFound()
            return real_incr(key)

        with patch.object(self.cache, 'incr', fake_incr):
            # Forcibly evict the key before incr() is called, but after the
            # initial add() call inside record_usage().
            evicted = True
            ratelimit.record_usage('a', ts)
            self.assertEquals(1, self.cache['rl:a-000000'])
コード例 #10
0
    def _has_exceeded_ratelimit(self, form, room):
        # grab the ratelimit (as average events per second) for the room's
        # current level, using the highest level configured that's not bigger
        # than the room.  e.g. if ratelimits are defined for levels 1, 2, and 4
        # and the room is level 3, this will give us the ratelimit specified
        # for 2.
        desired_avg_per_sec = 1
        by_level = g.live_config.get("robin_ratelimit_avg_per_sec", {})
        for level, avg_per_sec in sorted(by_level.items(),
                                         key=lambda (x, y): int(x)):
            if int(level) > room.level:
                break
            desired_avg_per_sec = avg_per_sec

        # now figure out how many events per window that means
        window_size = g.live_config.get("robin_ratelimit_window", 10)
        allowed_events_per_window = int(desired_avg_per_sec * window_size)

        try:
            # now figure out how much they've actually used
            ratelimit_key = "robin/{}".format(c.user._id36)
            time_slice = ratelimit.get_timeslice(window_size)
            usage = ratelimit.get_usage(ratelimit_key, time_slice)

            # ratelimit them if too much
            if usage >= allowed_events_per_window:
                g.stats.simple_event("robin.ratelimit.exceeded")

                period_end = datetime.datetime.utcfromtimestamp(time_slice.end)
                period_end_utc = period_end.replace(tzinfo=pytz.UTC)
                until_reset = utils.timeuntil(period_end_utc)
                c.errors.add(errors.RATELIMIT, {"time": until_reset},
                             field="ratelimit",
                             code=429)
                form.has_errors("ratelimit", errors.RATELIMIT)

                return True

            # or record the usage and move on
            ratelimit.record_usage(ratelimit_key, time_slice)
        except ratelimit.RatelimitError as exc:
            g.log.warning("ratelimit error: %s", exc)
        return False
コード例 #11
0
    def _has_exceeded_ratelimit(self, form, room):
        # grab the ratelimit (as average events per second) for the room's
        # current level, using the highest level configured that's not bigger
        # than the room.  e.g. if ratelimits are defined for levels 1, 2, and 4
        # and the room is level 3, this will give us the ratelimit specified
        # for 2.
        desired_avg_per_sec = 1
        by_level = g.live_config.get("robin_ratelimit_avg_per_sec", {})
        for level, avg_per_sec in sorted(by_level.items(), key=lambda (x,y): int(x)):
            if int(level) > room.level:
                break
            desired_avg_per_sec = avg_per_sec

        # now figure out how many events per window that means
        window_size = g.live_config.get("robin_ratelimit_window", 10)
        allowed_events_per_window = int(desired_avg_per_sec * window_size)

        try:
            # now figure out how much they've actually used
            ratelimit_key = "robin/{}".format(c.user._id36)
            time_slice = ratelimit.get_timeslice(window_size)
            usage = ratelimit.get_usage(ratelimit_key, time_slice)

            # ratelimit them if too much
            if usage >= allowed_events_per_window:
                g.stats.simple_event("robin.ratelimit.exceeded")

                period_end = datetime.datetime.utcfromtimestamp(time_slice.end)
                period_end_utc = period_end.replace(tzinfo=pytz.UTC)
                until_reset = utils.timeuntil(period_end_utc)
                c.errors.add(errors.RATELIMIT, {"time": until_reset},
                             field="ratelimit", code=429)
                form.has_errors("ratelimit", errors.RATELIMIT)

                return True

            # or record the usage and move on
            ratelimit.record_usage(ratelimit_key, time_slice)
        except ratelimit.RatelimitError as exc:
            g.log.warning("ratelimit error: %s", exc)
        return False
コード例 #12
0
 def test_append_time_slice_1s(self):
     self.now = 14
     ts = ratelimit.get_timeslice(1)
     key = ratelimit._append_time_slice('a', ts)
     self.assertEquals('a-000014', key)
コード例 #13
0
 def test_append_time_slice_1h(self):
     self.now = 3650
     ts = ratelimit.get_timeslice(3600)
     key = ratelimit._append_time_slice('a', ts)
     self.assertEquals('a-010000', key)
コード例 #14
0
 def test_append_time_slice_1w(self):
     self.now = 7 * 24 * 3600 + 5
     ts = ratelimit.get_timeslice(24 * 3600)
     key = ratelimit._append_time_slice('a', ts)
     self.assertEquals('a-@604800', key)
コード例 #15
0
ファイル: ratelimit_test.py プロジェクト: zeantsoi/reddit
 def test_make_ratelimit_cache_key_1w(self):
     self.now = 7 * 24 * 3600 + 5
     ts = ratelimit.get_timeslice(24 * 3600)
     key = ratelimit._make_ratelimit_cache_key('a', ts)
     self.assertEquals('rl:a-@604800', key)
コード例 #16
0
ファイル: ratelimit_test.py プロジェクト: zeantsoi/reddit
 def test_make_ratelimit_cache_key_1h(self):
     self.now = 3650
     ts = ratelimit.get_timeslice(3600)
     key = ratelimit._make_ratelimit_cache_key('a', ts)
     self.assertEquals('rl:a-010000', key)
コード例 #17
0
ファイル: ratelimit_test.py プロジェクト: zeantsoi/reddit
 def test_make_ratelimit_cache_key_1m(self):
     self.now = 65
     ts = ratelimit.get_timeslice(60)
     key = ratelimit._make_ratelimit_cache_key('a', ts)
     self.assertEquals('rl:a-000100', key)
コード例 #18
0
ファイル: ratelimit_test.py プロジェクト: zeantsoi/reddit
 def test_make_ratelimit_cache_key_1s(self):
     self.now = 14
     ts = ratelimit.get_timeslice(1)
     key = ratelimit._make_ratelimit_cache_key('a', ts)
     self.assertEquals('rl:a-000014', key)
コード例 #19
0
ファイル: ratelimit_test.py プロジェクト: zeantsoi/reddit
 def test_get_timeslice(self):
     self.now = 125
     ts = ratelimit.get_timeslice(60)
     self.assertEquals(120, ts.beginning)
     self.assertEquals(180, ts.end)
     self.assertEquals(55, ts.remaining)
コード例 #20
0
ファイル: ratelimit_test.py プロジェクト: zeantsoi/reddit
 def test_get_usage(self):
     self.now = 24 * 3600 + 5 * 3600
     ts = ratelimit.get_timeslice(3600)
     self.assertEquals(None, ratelimit.get_usage('a', ts))
     ratelimit.record_usage('a', ts)
     self.assertEquals(1, ratelimit.get_usage('a', ts))
コード例 #21
0
 def test_append_time_slice_1m(self):
     self.now = 65
     ts = ratelimit.get_timeslice(60)
     key = ratelimit._append_time_slice('a', ts)
     self.assertEquals('a-000100', key)