feat: added some rate limiting
parent
5b4fe4f442
commit
fbbda7a17c
|
@ -11,6 +11,7 @@ from fediseer.utils import hash_api_key
|
|||
from fediseer.messaging import activitypub_pm
|
||||
from pythorhead import Lemmy
|
||||
from fediseer.fediverse import get_admin_for_software, get_nodeinfo
|
||||
from fediseer.limiter import limiter
|
||||
|
||||
api = Namespace('v1', 'API Version 1' )
|
||||
|
||||
|
@ -22,6 +23,7 @@ handle_bad_request = api.errorhandler(e.BadRequest)(e.handle_bad_requests)
|
|||
handle_forbidden = api.errorhandler(e.Forbidden)(e.handle_bad_requests)
|
||||
handle_unauthorized = api.errorhandler(e.Unauthorized)(e.handle_bad_requests)
|
||||
handle_not_found = api.errorhandler(e.NotFound)(e.handle_bad_requests)
|
||||
handle_too_many_requests = api.errorhandler(e.TooManyRequests)(e.handle_bad_requests)
|
||||
handle_internal_server_error = api.errorhandler(e.InternalServerError)(e.handle_bad_requests)
|
||||
handle_service_unavailable = api.errorhandler(e.ServiceUnavailable)(e.handle_bad_requests)
|
||||
|
||||
|
|
|
@ -108,6 +108,7 @@ class Censures(Resource):
|
|||
return {"domains": [instance["domain"] for instance in instance_details]},200
|
||||
return {"instances": instance_details},200
|
||||
|
||||
decorators = [limiter.limit("20/minute", key_func = get_request_path)]
|
||||
put_parser = reqparse.RequestParser()
|
||||
put_parser.add_argument("apikey", type=str, required=True, help="The sending instance's API key.", location='headers')
|
||||
put_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
|
||||
|
@ -135,6 +136,8 @@ class Censures(Resource):
|
|||
raise e.Forbidden("Only guaranteed instances can censure others.")
|
||||
if instance.domain == domain:
|
||||
raise e.BadRequest("You're a mad lad, but you can't censure yourself.")
|
||||
if database.has_too_many_actions_per_min(instance.domain):
|
||||
raise e.TooManyRequests("Your instance is doing more than 20 actions per minute. Please slow down.")
|
||||
unbroken_chain, chainbreaker = database.has_unbroken_chain(instance.id)
|
||||
if not unbroken_chain:
|
||||
raise e.Forbidden(f"Guarantee chain for this instance has been broken. Chain ends at {chainbreaker.domain}!")
|
||||
|
@ -173,6 +176,7 @@ class Censures(Resource):
|
|||
return {"message":'Changed'}, 200
|
||||
|
||||
|
||||
decorators = [limiter.limit("20/minute", key_func = get_request_path)]
|
||||
patch_parser = reqparse.RequestParser()
|
||||
patch_parser.add_argument("apikey", type=str, required=True, help="The sending instance's API key.", location='headers')
|
||||
patch_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
|
||||
|
@ -195,6 +199,8 @@ class Censures(Resource):
|
|||
instance = database.find_instance_by_api_key(self.args.apikey)
|
||||
if not instance:
|
||||
raise e.NotFound(f"No Instance found matching provided API key and domain. Have you remembered to register it?")
|
||||
if database.has_too_many_actions_per_min(instance.domain):
|
||||
raise e.TooManyRequests("Your instance is doing more than 20 actions per minute. Please slow down.")
|
||||
target_instance = database.find_instance_by_domain(domain=domain)
|
||||
if not target_instance:
|
||||
raise e.BadRequest("Instance from which to modify censure not found")
|
||||
|
@ -228,6 +234,7 @@ class Censures(Resource):
|
|||
return {"message":'Changed'}, 200
|
||||
|
||||
|
||||
decorators = [limiter.limit("20/minute", key_func = get_request_path)]
|
||||
delete_parser = reqparse.RequestParser()
|
||||
delete_parser.add_argument("apikey", type=str, required=True, help="The sending instance's API key.", location='headers')
|
||||
delete_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
|
||||
|
|
|
@ -87,6 +87,7 @@ class Endorsements(Resource):
|
|||
return {"domains": [instance["domain"] for instance in instance_details]},200
|
||||
return {"instances": instance_details},200
|
||||
|
||||
decorators = [limiter.limit("20/minute", key_func = get_request_path)]
|
||||
put_parser = reqparse.RequestParser()
|
||||
put_parser.add_argument("apikey", type=str, required=True, help="The sending instance's API key.", location='headers')
|
||||
put_parser.add_argument("reason", default=None, type=str, required=False, location="json")
|
||||
|
@ -113,6 +114,8 @@ class Endorsements(Resource):
|
|||
raise e.Forbidden("Only guaranteed instances can endorse others.")
|
||||
if instance.domain == domain:
|
||||
raise e.BadRequest("Nice try, but you can't endorse yourself.")
|
||||
if database.has_too_many_actions_per_min(instance.domain):
|
||||
raise e.TooManyRequests("Your instance is doing more than 20 actions per minute. Please slow down.")
|
||||
unbroken_chain, chainbreaker = database.has_unbroken_chain(instance.id)
|
||||
if not unbroken_chain:
|
||||
raise e.Forbidden(f"Guarantee chain for this instance has been broken. Chain ends at {chainbreaker.domain}!")
|
||||
|
@ -160,6 +163,7 @@ class Endorsements(Resource):
|
|||
logger.info(f"{instance.domain} Endorsed {domain}")
|
||||
return {"message":'Changed'}, 200
|
||||
|
||||
decorators = [limiter.limit("20/minute", key_func = get_request_path)]
|
||||
patch_parser = reqparse.RequestParser()
|
||||
patch_parser.add_argument("apikey", type=str, required=True, help="The sending instance's API key.", location='headers')
|
||||
patch_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
|
||||
|
@ -181,6 +185,8 @@ class Endorsements(Resource):
|
|||
instance = database.find_instance_by_api_key(self.args.apikey)
|
||||
if not instance:
|
||||
raise e.NotFound(f"No Instance found matching provided API key and domain. Have you remembered to register it?")
|
||||
if database.has_too_many_actions_per_min(instance.domain):
|
||||
raise e.TooManyRequests("Your instance is doing more than 20 actions per minute. Please slow down.")
|
||||
target_instance = database.find_instance_by_domain(domain=domain)
|
||||
if not target_instance:
|
||||
raise e.BadRequest("Instance for which to modify endorsement not found")
|
||||
|
@ -208,7 +214,7 @@ class Endorsements(Resource):
|
|||
return {"message":'Changed'}, 200
|
||||
|
||||
|
||||
|
||||
decorators = [limiter.limit("20/minute", key_func = get_request_path)]
|
||||
delete_parser = reqparse.RequestParser()
|
||||
delete_parser.add_argument("apikey", type=str, required=True, help="The sending instance's API key.", location='headers')
|
||||
delete_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
|
||||
|
@ -227,6 +233,8 @@ class Endorsements(Resource):
|
|||
instance = database.find_instance_by_api_key(self.args.apikey)
|
||||
if not instance:
|
||||
raise e.NotFound(f"No Instance found matching provided API key and domain. Have you remembered to register it?")
|
||||
if database.has_too_many_actions_per_min(instance.domain):
|
||||
raise e.TooManyRequests("Your instance is doing more than 20 actions per minute. Please slow down.")
|
||||
target_instance = database.find_instance_by_domain(domain=domain)
|
||||
if not target_instance:
|
||||
raise e.BadRequest("Instance from which to withdraw endorsement not found")
|
||||
|
|
|
@ -56,6 +56,7 @@ class Guarantees(Resource):
|
|||
logger.debug(database.get_guarantor_chain(instance.id))
|
||||
return {"instances": instance_details},200
|
||||
|
||||
decorators = [limiter.limit("20/minute", key_func = get_request_path)]
|
||||
put_parser = reqparse.RequestParser()
|
||||
put_parser.add_argument("apikey", type=str, required=True, help="The sending instance's API key.", location='headers')
|
||||
put_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
|
||||
|
@ -83,6 +84,8 @@ class Guarantees(Resource):
|
|||
raise e.Forbidden("Only guaranteed instances can guarantee others.")
|
||||
if len(instance.guarantees) >= 20 and instance.id != 0:
|
||||
raise e.Forbidden("You cannot guarantee for more than 20 instances")
|
||||
if database.has_too_many_actions_per_min(instance.domain):
|
||||
raise e.TooManyRequests("Your instance is doing more than 20 actions per minute. Please slow down.")
|
||||
unbroken_chain, chainbreaker = database.has_unbroken_chain(instance.id)
|
||||
if not unbroken_chain:
|
||||
raise e.Forbidden(f"Guarantee chain for this instance has been broken. Chain ends at {chainbreaker.domain}!")
|
||||
|
@ -133,6 +136,7 @@ class Guarantees(Resource):
|
|||
return {"message":'Changed'}, 200
|
||||
|
||||
|
||||
decorators = [limiter.limit("20/minute", key_func = get_request_path)]
|
||||
delete_parser = reqparse.RequestParser()
|
||||
delete_parser.add_argument("apikey", type=str, required=True, help="The sending instance's API key.", location='headers')
|
||||
delete_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
|
||||
|
@ -151,6 +155,8 @@ class Guarantees(Resource):
|
|||
instance = database.find_instance_by_api_key(self.args.apikey)
|
||||
if not instance:
|
||||
raise e.NotFound(f"No Instance found matching provided API key and domain. Have you remembered to register it?")
|
||||
if database.has_too_many_actions_per_min(instance.domain):
|
||||
raise e.TooManyRequests("Your instance is doing more than 20 actions per minute. Please slow down.")
|
||||
target_instance = database.find_instance_by_domain(domain=domain)
|
||||
if not target_instance:
|
||||
raise e.BadRequest("Instance from which to withdraw endorsement not found")
|
||||
|
@ -184,7 +190,7 @@ class Guarantees(Resource):
|
|||
db.session.add(solicitation_report)
|
||||
|
||||
db.session.delete(guarantee)
|
||||
rejection_record = database.get_rejection_record(instance.id,target_instance.id)
|
||||
rejection_recorinstanced = database.get_rejection_record(instance.id,target_instance.id)
|
||||
if rejection_record:
|
||||
rejection_record.refresh()
|
||||
else:
|
||||
|
|
|
@ -93,6 +93,7 @@ class Hesitations(Resource):
|
|||
return {"domains": [instance["domain"] for instance in instance_details]},200
|
||||
return {"instances": instance_details},200
|
||||
|
||||
decorators = [limiter.limit("20/minute", key_func = get_request_path)]
|
||||
put_parser = reqparse.RequestParser()
|
||||
put_parser.add_argument("apikey", type=str, required=True, help="The sending instance's API key.", location='headers')
|
||||
put_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
|
||||
|
@ -120,6 +121,8 @@ class Hesitations(Resource):
|
|||
raise e.Forbidden("Only guaranteed instances can hesitation others.")
|
||||
if instance.domain == domain:
|
||||
raise e.BadRequest("You're a mad lad, but you can't hesitation yourself.")
|
||||
if database.has_too_many_actions_per_min(instance.domain):
|
||||
raise e.TooManyRequests("Your instance is doing more than 20 actions per minute. Please slow down.")
|
||||
unbroken_chain, chainbreaker = database.has_unbroken_chain(instance.id)
|
||||
if not unbroken_chain:
|
||||
raise e.Forbidden(f"Guarantee chain for this instance has been broken. Chain ends at {chainbreaker.domain}!")
|
||||
|
@ -157,7 +160,7 @@ class Hesitations(Resource):
|
|||
logger.info(f"{instance.domain} hesitated against {domain}")
|
||||
return {"message":'Changed'}, 200
|
||||
|
||||
|
||||
decorators = [limiter.limit("20/minute", key_func = get_request_path)]
|
||||
patch_parser = reqparse.RequestParser()
|
||||
patch_parser.add_argument("apikey", type=str, required=True, help="The sending instance's API key.", location='headers')
|
||||
patch_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
|
||||
|
@ -180,6 +183,8 @@ class Hesitations(Resource):
|
|||
instance = database.find_instance_by_api_key(self.args.apikey)
|
||||
if not instance:
|
||||
raise e.NotFound(f"No Instance found matching provided API key and domain. Have you remembered to register it?")
|
||||
if database.has_too_many_actions_per_min(instance.domain):
|
||||
raise e.TooManyRequests("Your instance is doing more than 20 actions per minute. Please slow down.")
|
||||
target_instance = database.find_instance_by_domain(domain=domain)
|
||||
if not target_instance:
|
||||
raise e.BadRequest("Instance from which to modify hesitation not found")
|
||||
|
@ -212,7 +217,7 @@ class Hesitations(Resource):
|
|||
logger.info(f"{instance.domain} modIfied hesitation for {domain}")
|
||||
return {"message":'Changed'}, 200
|
||||
|
||||
|
||||
decorators = [limiter.limit("20/minute", key_func = get_request_path)]
|
||||
delete_parser = reqparse.RequestParser()
|
||||
delete_parser.add_argument("apikey", type=str, required=True, help="The sending instance's API key.", location='headers')
|
||||
delete_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
|
||||
|
@ -231,6 +236,8 @@ class Hesitations(Resource):
|
|||
instance = database.find_instance_by_api_key(self.args.apikey)
|
||||
if not instance:
|
||||
raise e.NotFound(f"No Instance found matching provided API key and domain. Have you remembered to register it?")
|
||||
if database.has_too_many_actions_per_min(instance.domain):
|
||||
raise e.TooManyRequests("Your instance is doing more than 20 actions per minute. Please slow down.")
|
||||
target_instance = database.find_instance_by_domain(domain=domain)
|
||||
if not target_instance:
|
||||
raise e.BadRequest("Instance from which to withdraw hesitation not found")
|
||||
|
|
|
@ -28,6 +28,7 @@ class Solicitations(Resource):
|
|||
return {"domains": [instance["domain"] for instance in instance_details]},200
|
||||
return {"instances": instance_details},200
|
||||
|
||||
decorators = [limiter.limit("20/minute", key_func = get_request_path)]
|
||||
post_parser = reqparse.RequestParser()
|
||||
post_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
|
||||
post_parser.add_argument("apikey", type=str, required=True, help="The sending instance's API key.", location='headers')
|
||||
|
@ -54,6 +55,8 @@ class Solicitations(Resource):
|
|||
raise e.NotFound(f"No Instance found matching provided API key and domain. Have you remembered to claim it?")
|
||||
if instance.is_guaranteed():
|
||||
raise e.BadRequest(f"Your instance is already guaranteed by {instance.get_guarantor().domain}")
|
||||
if database.has_too_many_actions_per_min(instance.domain):
|
||||
raise e.TooManyRequests("Your instance is doing more than 20 actions per minute. Please slow down.")
|
||||
guarantor_instance = None
|
||||
if self.args.guarantor:
|
||||
guarantor_instance = database.find_instance_by_domain(self.args.guarantor)
|
||||
|
|
|
@ -446,4 +446,12 @@ def find_latest_solicitation_by_source(source_id):
|
|||
).filter(
|
||||
Solicitation.source_id == source_id,
|
||||
)
|
||||
return query.order_by(Solicitation.created.desc()).first()
|
||||
return query.order_by(Solicitation.created.desc()).first()
|
||||
|
||||
def has_too_many_actions_per_min(source_domain):
|
||||
query = Report.query.filter_by(
|
||||
source_domain=source_domain
|
||||
).filter(
|
||||
Report.created > datetime.utcnow() - timedelta(minutes=1),
|
||||
)
|
||||
return query.count() > 20
|
|
@ -26,6 +26,11 @@ class Locked(wze.Locked):
|
|||
self.specific = message
|
||||
self.log = log
|
||||
|
||||
class TooManyRequests(wze.TooManyRequests):
|
||||
def __init__(self, message, log=None):
|
||||
self.specific = message
|
||||
self.log = log
|
||||
|
||||
class InternalServerError(wze.InternalServerError):
|
||||
def __init__(self, message, log=None):
|
||||
self.specific = message
|
||||
|
|
Loading…
Reference in New Issue