-
Notifications
You must be signed in to change notification settings - Fork 449
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
feat: Add rate limit to endpoints #1045
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,8 @@ | |
from flask_jwt_extended import jwt_required, get_jwt_identity | ||
from http import HTTPStatus | ||
|
||
from app import rate_limits | ||
from app.rate_limiter import limiter | ||
from app import messages | ||
from app.api.resources.common import auth_header_parser | ||
from app.api.dao.mentorship_relation import MentorshipRelationDAO | ||
|
@@ -24,6 +26,8 @@ | |
|
||
@mentorship_relation_ns.route("mentorship_relation/send_request") | ||
class SendRequest(Resource): | ||
decorators = [limiter.limit(rate_limits.LIMIT_1)] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jalajcodes , I don't see this variable "decorators" being used anywhere in the code. Do we need to declare it as variable? Can we use the |
||
|
||
@classmethod | ||
@jwt_required | ||
@mentorship_relation_ns.doc("send_request") | ||
|
@@ -121,6 +125,8 @@ def is_valid_data(data): | |
|
||
@mentorship_relation_ns.route("mentorship_relations") | ||
class GetAllMyMentorshipRelation(Resource): | ||
decorators = [limiter.limit(rate_limits.LIMIT_1)] | ||
|
||
@classmethod | ||
@jwt_required | ||
@mentorship_relation_ns.doc("get_all_user_mentorship_relations") | ||
|
@@ -173,6 +179,8 @@ def get(cls): | |
|
||
@mentorship_relation_ns.route("mentorship_relation/<int:request_id>/accept") | ||
class AcceptMentorshipRelation(Resource): | ||
decorators = [limiter.limit(rate_limits.LIMIT_1)] | ||
|
||
@classmethod | ||
@jwt_required | ||
@mentorship_relation_ns.doc("accept_mentorship_relation") | ||
|
@@ -222,6 +230,8 @@ def put(cls, request_id): | |
|
||
@mentorship_relation_ns.route("mentorship_relation/<int:request_id>/reject") | ||
class RejectMentorshipRelation(Resource): | ||
decorators = [limiter.limit(rate_limits.LIMIT_1)] | ||
|
||
@classmethod | ||
@jwt_required | ||
@mentorship_relation_ns.doc("reject_mentorship_relation") | ||
|
@@ -266,6 +276,8 @@ def put(cls, request_id): | |
|
||
@mentorship_relation_ns.route("mentorship_relation/<int:request_id>/cancel") | ||
class CancelMentorshipRelation(Resource): | ||
decorators = [limiter.limit(rate_limits.LIMIT_1)] | ||
|
||
@classmethod | ||
@jwt_required | ||
@mentorship_relation_ns.doc("cancel_mentorship_relation") | ||
|
@@ -309,6 +321,8 @@ def put(cls, request_id): | |
|
||
@mentorship_relation_ns.route("mentorship_relation/<int:request_id>") | ||
class DeleteMentorshipRelation(Resource): | ||
decorators = [limiter.limit(rate_limits.LIMIT_1)] | ||
|
||
@classmethod | ||
@jwt_required | ||
@mentorship_relation_ns.doc("delete_mentorship_relation") | ||
|
@@ -352,6 +366,8 @@ def delete(cls, request_id): | |
|
||
@mentorship_relation_ns.route("mentorship_relations/past") | ||
class ListPastMentorshipRelations(Resource): | ||
decorators = [limiter.limit(rate_limits.LIMIT_1)] | ||
|
||
@classmethod | ||
@jwt_required | ||
@mentorship_relation_ns.doc("get_past_mentorship_relations") | ||
|
@@ -391,6 +407,8 @@ def get(cls): | |
|
||
@mentorship_relation_ns.route("mentorship_relations/current") | ||
class ListCurrentMentorshipRelation(Resource): | ||
decorators = [limiter.limit(rate_limits.LIMIT_1)] | ||
|
||
@classmethod | ||
@jwt_required | ||
@mentorship_relation_ns.doc("get_current_mentorship_relation") | ||
|
@@ -431,6 +449,8 @@ def get(cls): | |
|
||
@mentorship_relation_ns.route("mentorship_relations/pending") | ||
class ListPendingMentorshipRequests(Resource): | ||
decorators = [limiter.limit(rate_limits.LIMIT_1)] | ||
|
||
@classmethod | ||
@jwt_required | ||
@mentorship_relation_ns.doc("get_pending_mentorship_relations") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
from flask_limiter import Limiter | ||
from flask_limiter.util import get_remote_address | ||
from flask import jsonify, make_response, request | ||
|
||
from app.rate_limits import GLOBAL_LIMIT | ||
|
||
limiter = Limiter(key_func=get_remote_address, default_limits=GLOBAL_LIMIT) | ||
|
||
|
||
def rate_limit_exceeded(e): | ||
return make_response(jsonify(error=f"rate limit exceeded: {e.description}"), 429) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
GLOBAL_LIMIT = ["200 per day", "50 per hour"] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jalajcodes , can you please tell me how you decided to use these numbers (200/day, 50/hr)? What is the assumption on the number of users for this one? I'm assuming that the rate limiter does not look on a particular user's request, but for the requests coming in to that endpoint regardless of IP address of the requesters (please do correct me if I'm wrong)? If this is so, what if we have a large number of users (e.g. > 200) that means some of our users won't be able to use the app if the rate limit has been reached, right? This could raise issue 🤔. Can you see if you can apply the rate limiter based on specific IP address? This GitHub discussion indicated that the default which limit per IP address has been deprecated but can still be used providing you explicitly use it in your code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is applied based on the ip address afaik. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @mtreacy002,
As I mentioned in the description, these numbers are just example values, we need to brainstorm appropriate rate limits before this PR is merged.
rate limit is based on IP address, so 200 requests per day for an ip addr. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 200 is still pretty high for login and signup. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, its done via this line There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Thats global limit, we override this in login route |
||
|
||
LIMIT_1 = "20 per minute" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you create a doc listing the rates for each endpoint or display the rate limit on the swagger UI?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will create the doc, but I will need help in deciding the rate limit for each endpoint
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can ask it on zulip itself