Ejemplo n.º 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
Ejemplo n.º 2
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
Ejemplo n.º 3
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
Ejemplo n.º 4
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
Ejemplo n.º 5
0
    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, delta=1):
            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'])
Ejemplo n.º 6
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
Ejemplo n.º 7
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
Ejemplo n.º 8
0
    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'])
Ejemplo n.º 9
0
 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))