Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add user level rate limiting #10

Open
dreaminglucid opened this issue Aug 10, 2023 · 1 comment
Open

Add user level rate limiting #10

dreaminglucid opened this issue Aug 10, 2023 · 1 comment
Assignees
Labels
bug Something isn't working enhancement New feature or request

Comments

@dreaminglucid
Copy link
Owner

dreaminglucid commented Aug 10, 2023

To add a user-level rate-limiting mechanism to your Flask application without using external services like Redis, you can use Python dictionaries to keep track of API call counts and timestamps for each user. Below is how to do it:

Rate-Limiting Strategy:

Maintain a Python dictionary, user_access, to store the following information per user:

  • count: Number of API calls made.
  • timestamp: Timestamp of the first API call within the current time window.

For each API call:

  • Check the count and timestamp for the user.
  • If the count is within the limit and the time window hasn't expired, allow the call.
  • Otherwise, deny the call and return a 429 'Too Many Requests' status.

Implementation:

First, let's create a rate-limiting decorator function:

from datetime import datetime, timedelta
from functools import wraps

user_access = {}  # Dictionary to store rate-limiting data
RATE_LIMIT = 5  # Number of API calls allowed per time window
TIME_WINDOW = timedelta(seconds=60)  # Time window duration

def rate_limit(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        global user_access

        # Extract user email (modify this based on how you identify users)
        id_token = kwargs.get("userEmail", None)
        
        if id_token:
            current_time = datetime.now()

            # Initialize user data if not present
            if id_token not in user_access:
                user_access[id_token] = {'count': 0, 'timestamp': current_time}

            user_data = user_access[id_token]

            # Reset count and timestamp if the time window has expired
            if current_time - user_data['timestamp'] > TIME_WINDOW:
                user_data['count'] = 0
                user_data['timestamp'] = current_time

            # Check the API call count
            if user_data['count'] < RATE_LIMIT:
                user_data['count'] += 1
                return func(*args, **kwargs)
            else:
                return jsonify({"error": "Rate limit exceeded"}), 429
        else:
            return jsonify({"error": "User identification failed"}), 401

    return wrapper

Now, apply this decorator to your endpoints where you want rate-limiting:

@app.route("/api/dreams", methods=["POST"], endpoint='create_dream_endpoint')
@handle_jwt_token
@rate_limit  # Apply rate-limiting here
@use_args(dream_args)
def create_dream_endpoint(args, userEmail):
    # Your existing code here

This should effectively rate-limit your API endpoints on a per-user basis. Since Python dictionaries are not persistent, this rate-limiting will reset if the server restarts. For a more persistent solution, you would typically use a database or cache like Redis, but for a simple and robust solution, this should suffice.

@dreaminglucid dreaminglucid changed the title Add rate limiting Add user level rate limiting Aug 10, 2023
@dreaminglucid dreaminglucid added the enhancement New feature or request label Aug 13, 2023
@dreaminglucid dreaminglucid self-assigned this Aug 13, 2023
@dreaminglucid
Copy link
Owner Author

We may need to consider the situation where a user changes their time zone. This could be potentially manipulated to game the system.

@dreaminglucid dreaminglucid added the bug Something isn't working label Aug 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant