Exemple #1
0
def test_check_for_ip_invalid_address():
    """Ensure RateLimitedAction.check_for_ip can't take an invalid IP."""
    ip = '123.456.789.123'

    action = RateLimitedAction('testaction', timedelta(hours=1), 10)

    with raises(ValueError):
        action.check_for_ip(ip)
Exemple #2
0
def test_reset_for_ip_invalid_address():
    """Ensure RateLimitedAction.reset_for_ip can't take an invalid IP."""
    ip = "123.456.789.123"

    action = RateLimitedAction("testaction", timedelta(hours=1), 10)

    with raises(ValueError):
        action.reset_for_ip(ip)
Exemple #3
0
def test_different_user_ids_limited_separately(redis):
    """Ensure one user being rate-limited doesn't affect a different one."""
    limit = 5
    user_id = 1

    action = RateLimitedAction("test", timedelta(hours=1), limit, redis=redis)

    # check the action for the first user_id until it's blocked
    result = action.check_for_user_id(user_id)
    while result.is_allowed:
        result = action.check_for_user_id(user_id)

    # it should still be allowed for a different user_id
    assert action.check_for_user_id(user_id + 1)
Exemple #4
0
def test_remaining_limit(redis):
    """Ensure a limit's "remaining limit" decreases as expected."""
    user_id = 1
    limit = 10

    # create an action allowing the full limit as a burst
    action = RateLimitedAction("test",
                               timedelta(days=1),
                               limit,
                               max_burst=limit,
                               redis=redis)

    for count in range(1, limit + 1):
        result = action.check_for_user_id(user_id)
        assert result.remaining_limit == limit - count
Exemple #5
0
def test_max_burst_defaults_to_half(redis):
    """Ensure that unspecified max_burst on a RateLimitedAction allows half."""
    limit = 10
    user_id = 1

    action = RateLimitedAction("test", timedelta(days=1), limit, redis=redis)

    # see how many times we can do the action until it gets blocked
    count = 0
    while True:
        result = action.check_for_user_id(user_id)
        if result.is_allowed:
            count += 1
        else:
            break

    assert count == limit // 2
Exemple #6
0
def test_action_with_all_types_disabled():
    """Ensure RateLimitedAction can't have both by_user and by_ip disabled."""
    with raises(ValueError):
        RateLimitedAction("test",
                          timedelta(hours=1),
                          5,
                          by_user=False,
                          by_ip=False)
Exemple #7
0
def test_simple_rate_limiting_by_user_id(redis):
    """Ensure simple rate-limiting by user_id is working."""
    limit = 5
    user_id = 1

    # define an action with max_burst equal to the full limit
    action = RateLimitedAction("testaction",
                               timedelta(hours=1),
                               limit,
                               max_burst=limit,
                               redis=redis)

    # run the action the full number of times, should all be allowed
    for _ in range(limit):
        result = action.check_for_user_id(user_id)
        assert result.is_allowed

    # try one more time, should be rejected
    result = action.check_for_user_id(user_id)
    assert not result.is_allowed
Exemple #8
0
def check_rate_limit(request: Request, action_name: str) -> RateLimitResult:
    """Check the rate limit for a particular action on a request."""
    action = None

    # check for a custom rate-limit for the user
    if request.user:
        user_limit = (request.query(UserRateLimit).filter(
            UserRateLimit.user == request.user,
            UserRateLimit.action == action_name).one_or_none())

        if user_limit:
            action = RateLimitedAction(
                action_name,
                user_limit.period,
                user_limit.limit,
                by_user=True,
                by_ip=False,
            )

    # if a custom rate-limit wasn't found, use the default, global rate-limit
    if not action:
        try:
            action = RATE_LIMITED_ACTIONS[action_name]
        except KeyError:
            raise ValueError("Invalid action name: %s" % action_name)

    action.redis = request.redis

    results = []

    if action.by_user and request.user:
        results.append(action.check_for_user_id(request.user.user_id))

    if action.by_ip and request.remote_addr:
        results.append(action.check_for_ip(request.remote_addr))

    # no checks were done, return the "not limited" result
    if not results:
        return RateLimitResult.unlimited_result()

    return RateLimitResult.merged_result(results)
Exemple #9
0
def test_time_until_retry(redis):
    """Ensure an unbursted limit's time_until_retry is the expected value."""
    user_id = 1
    period = timedelta(seconds=60)
    limit = 2

    # create an action with no burst allowed, which will force the actions to be spaced
    # "evenly" across the limit
    action = RateLimitedAction("test",
                               period=period,
                               limit=limit,
                               max_burst=1,
                               redis=redis)

    # first usage should be fine
    result = action.check_for_user_id(user_id)
    assert result.is_allowed

    # second should fail, and require a wait of (period / limit) - 1 sec
    result = action.check_for_user_id(user_id)
    assert not result.is_allowed
    assert result.time_until_retry == (period / limit) - timedelta(seconds=1)
Exemple #10
0
def test_simple_global_rate_limiting(redis):
    """Ensure simple global rate-limiting is working."""
    limit = 5

    # define an action with max_burst equal to the full limit
    action = RateLimitedAction(
        "testaction",
        timedelta(hours=1),
        limit,
        max_burst=limit,
        by_user=False,
        by_ip=False,
        redis=redis,
    )

    # run the action the full number of times, should all be allowed
    for _ in range(limit):
        result = action.check_global()
        assert result.is_allowed

    # try one more time, should be rejected
    result = action.check_global()
    assert not result.is_allowed
Exemple #11
0
def test_check_global_disabled():
    """Ensure global check is disabled if action is by_user or by_ip."""
    action = RateLimitedAction("test",
                               timedelta(hours=1),
                               5,
                               by_user=True,
                               by_ip=False)
    with raises(RateLimitError):
        action.check_global()

    action = RateLimitedAction("test",
                               timedelta(hours=1),
                               5,
                               by_user=False,
                               by_ip=True)
    with raises(RateLimitError):
        action.check_global()
Exemple #12
0
def test_max_burst_with_limit_1():
    """Ensure an action with limit 1 also has its max_burst set to 1."""
    action = RateLimitedAction("test", timedelta(hours=1), 1)

    assert action.max_burst == 1
Exemple #13
0
def test_check_by_ip_disabled():
    """Ensure non-by_ip RateLimitedAction can't be checked by ip."""
    action = RateLimitedAction("test", timedelta(hours=1), 5, by_ip=False)

    with raises(RateLimitError):
        action.check_for_ip("123.123.123.123")
Exemple #14
0
def test_check_by_user_id_disabled():
    """Ensure non-by_user RateLimitedAction can't be checked by user_id."""
    action = RateLimitedAction("test", timedelta(hours=1), 5, by_user=False)

    with raises(RateLimitError):
        action.check_for_user_id(1)