From a40dee5bd8dae82acdd6a5d3a19718e9e34a2904 Mon Sep 17 00:00:00 2001 From: Aevann Date: Sat, 21 Jan 2023 06:39:46 +0200 Subject: [PATCH] improve user ratelimiting --- files/routes/admin.py | 50 +++++++++++++++++++++++++++ files/routes/asset_submissions.py | 12 +++++++ files/routes/awards.py | 4 ++- files/routes/casino.py | 12 +++++++ files/routes/chat.py | 7 +++- files/routes/comments.py | 17 ++++++---- files/routes/front.py | 3 ++ files/routes/giphy.py | 1 + files/routes/hats.py | 5 +++ files/routes/login.py | 3 +- files/routes/lottery.py | 5 +++ files/routes/mail.py | 3 +- files/routes/notifications.py | 10 ++++-- files/routes/oauth.py | 19 +++++++---- files/routes/polls.py | 4 +++ files/routes/posts.py | 22 +++++++----- files/routes/push_notifs.py | 1 + files/routes/reporting.py | 6 ++-- files/routes/search.py | 3 ++ files/routes/settings.py | 56 ++++++++++++++++++------------- files/routes/static.py | 17 +++++++++- files/routes/subs.py | 42 ++++++++++++++++++----- files/routes/users.py | 55 ++++++++++++++++++++++++------ files/routes/votes.py | 5 +-- files/routes/wrappers.py | 10 ++++++ 25 files changed, 298 insertions(+), 74 deletions(-) diff --git a/files/routes/admin.py b/files/routes/admin.py index fd7667e11..827ce3394 100644 --- a/files/routes/admin.py +++ b/files/routes/admin.py @@ -24,6 +24,7 @@ from files.routes.routehelpers import get_alt_graph, get_alt_graph_ids from .front import frontlist @app.get('/admin/loggedin') +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['VIEW_ACTIVE_USERS']) def loggedin_list(v): ids = [x for x,val in cache.get(f'{SITE}_loggedin').items() if time.time()-val < LOGGEDIN_ACTIVE_TIME] @@ -31,6 +32,7 @@ def loggedin_list(v): return render_template("admin/loggedin.html", v=v, users=users) @app.get('/admin/loggedout') +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['VIEW_ACTIVE_USERS']) def loggedout_list(v): users = sorted([val[1] for x,val in cache.get(f'{SITE}_loggedout').items() if time.time()-val[0] < LOGGEDIN_ACTIVE_TIME]) @@ -38,6 +40,7 @@ def loggedout_list(v): @app.get('/admin/move//') +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_MERGE']) def move_acc(v:User, new_id, old_id): if v.id != AEVANN_ID: abort(403) @@ -189,6 +192,7 @@ def move_acc(v:User, new_id, old_id): @app.get('/admin/edit_rules') +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['EDIT_RULES']) def edit_rules_get(v): try: @@ -201,6 +205,7 @@ def edit_rules_get(v): @app.post('/admin/edit_rules') @limiter.limit("1/second;30/minute;200/hour;1000/day") +@limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=get_ID) @admin_level_required(PERMS['EDIT_RULES']) def edit_rules_post(v): rules = request.values.get('rules', '').strip() @@ -217,6 +222,7 @@ def edit_rules_post(v): return render_template('admin/edit_rules.html', v=v, rules=rules, msg='Rules edited successfully!') @app.post("/@/make_admin") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['ADMIN_ADD']) def make_admin(v:User, username): if SITE == 'rdrama.net': abort(403) @@ -239,6 +245,7 @@ def make_admin(v:User, username): @app.post("/@/remove_admin") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['ADMIN_REMOVE']) def remove_admin(v:User, username): if SITE == 'devrama.net': @@ -266,6 +273,7 @@ def remove_admin(v:User, username): @app.post("/distribute/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['POST_BETS_DISTRIBUTE']) def distribute(v:User, option_id): autojanny = get_account(AUTOJANNY_ID) @@ -322,6 +330,7 @@ def distribute(v:User, option_id): @app.post("/@/revert_actions") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['ADMIN_ACTIONS_REVERT']) def revert_actions(v:User, username): revertee = get_user(username) @@ -374,6 +383,7 @@ def revert_actions(v:User, username): return {"message": f"@{revertee.username}'s admin actions have been reverted!"} @app.get("/admin/shadowbanned") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_SHADOWBAN']) def shadowbanned(v): users = g.db.query(User) \ @@ -386,6 +396,7 @@ def shadowbanned(v): @app.get("/admin/image_posts") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def image_posts_listing(v): @@ -404,6 +415,7 @@ def image_posts_listing(v): @app.get("/admin/reported/posts") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def reported_posts(v): @@ -427,6 +439,7 @@ def reported_posts(v): @app.get("/admin/reported/comments") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def reported_comments(v): @@ -454,6 +467,7 @@ def reported_comments(v): standalone=True) @app.get("/admin") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['ADMIN_HOME_VISIBLE']) def admin_home(v): under_attack = False @@ -465,6 +479,7 @@ def admin_home(v): under_attack=under_attack) @app.post("/admin/site_settings/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['SITE_SETTINGS']) def change_settings(v:User, setting): if SITE == 'devrama.net': @@ -483,6 +498,7 @@ def change_settings(v:User, setting): return {'message': f"{setting.replace('_', ' ').title()} {word}d successfully!"} @app.post("/admin/clear_cloudflare_cache") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['SITE_CACHE_PURGE_CDN']) def clear_cloudflare_cache(v): if not clear_entire_cache(): @@ -495,6 +511,7 @@ def clear_cloudflare_cache(v): return {"message": "Cloudflare cache cleared!"} @app.post("/admin/under_attack") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['SITE_SETTINGS_UNDER_ATTACK']) def under_attack(v): response = get_security_level() @@ -524,6 +541,7 @@ def admin_badges_grantable_list(v): @app.get("/admin/badge_grant") @app.get("/admin/badge_remove") @feature_required('BADGES') +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_BADGES']) def badge_grant_get(v): grant = request.url.endswith("grant") @@ -535,6 +553,7 @@ def badge_grant_get(v): @app.post("/admin/badge_grant") @feature_required('BADGES') @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['USER_BADGES']) def badge_grant_post(v): badges = admin_badges_grantable_list(v) @@ -595,6 +614,7 @@ def badge_grant_post(v): @app.post("/admin/badge_remove") @feature_required('BADGES') @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['USER_BADGES']) def badge_remove_post(v): badges = admin_badges_grantable_list(v) @@ -629,6 +649,7 @@ def badge_remove_post(v): @app.get("/admin/alt_votes") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['VIEW_ALT_VOTES']) def alt_votes_get(v): u1 = request.values.get("u1") @@ -735,6 +756,7 @@ def alt_votes_get(v): @app.get("/admin/alts/") @app.get("/@/alts/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['USER_LINK']) def admin_view_alts(v:User, username=None): u = get_user(username or request.values.get('username'), graceful=True) @@ -742,6 +764,7 @@ def admin_view_alts(v:User, username=None): @app.post('/@/alts/') @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['USER_LINK']) def admin_add_alt(v:User, username): user1 = get_user(username) @@ -781,6 +804,7 @@ def admin_add_alt(v:User, username): @app.route('/@/alts//deleted', methods=["PUT", "DELETE"]) @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['USER_LINK']) def admin_delink_relink_alt(v:User, username, other): is_delinking = request.method == 'PUT' # we're adding the 'deleted' state if a PUT request @@ -810,6 +834,7 @@ def admin_delink_relink_alt(v:User, username, other): @app.get("/admin/removed/posts") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def admin_removed(v): try: page = int(request.values.get("page", 1)) @@ -831,6 +856,7 @@ def admin_removed(v): @app.get("/admin/removed/comments") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def admin_removed_comments(v): try: page = int(request.values.get("page", 1)) @@ -849,6 +875,7 @@ def admin_removed_comments(v): ) @app.post("/unagendaposter/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_AGENDAPOSTER']) def unagendaposter(id, v): @@ -887,6 +914,7 @@ def unagendaposter(id, v): @app.post("/shadowban/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['USER_SHADOWBAN']) def shadowban(user_id, v): user = get_account(user_id) @@ -917,6 +945,7 @@ def shadowban(user_id, v): @app.post("/unshadowban/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['USER_SHADOWBAN']) def unshadowban(user_id, v): user = get_account(user_id) @@ -943,6 +972,7 @@ def unshadowban(user_id, v): @app.post("/admin/title_change/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['USER_TITLE_CHANGE']) def admin_title_change(user_id, v): @@ -979,6 +1009,7 @@ def admin_title_change(user_id, v): @app.post("/ban_user/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['USER_BAN']) def ban_user(id, v): @@ -1063,6 +1094,7 @@ def ban_user(id, v): @app.post("/agendaposter/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_AGENDAPOSTER']) def agendaposter(id, v): @@ -1150,6 +1182,7 @@ def agendaposter(id, v): @app.post("/unban_user/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['USER_BAN']) def unban_user(id, v): @@ -1194,6 +1227,7 @@ def unban_user(id, v): @app.post("/mute_user/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['USER_BAN']) def mute_user(v:User, user_id): user = get_account(user_id) @@ -1213,6 +1247,7 @@ def mute_user(v:User, user_id): @app.post("/unmute_user/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['USER_BAN']) def unmute_user(v:User, user_id): user = get_account(user_id) @@ -1232,6 +1267,7 @@ def unmute_user(v:User, user_id): @app.post("/remove_post/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def remove_post(post_id, v): post = get_post(post_id) @@ -1260,6 +1296,7 @@ def remove_post(post_id, v): @app.post("/approve_post/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def approve_post(post_id, v): post = get_post(post_id) @@ -1290,6 +1327,7 @@ def approve_post(post_id, v): @app.post("/distinguish/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_DISTINGUISH']) def distinguish_post(post_id, v): post = get_post(post_id) @@ -1317,6 +1355,7 @@ def distinguish_post(post_id, v): @app.post("/sticky/") @feature_required('PINS') +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def sticky_post(post_id, v): post = get_post(post_id) @@ -1357,6 +1396,7 @@ def sticky_post(post_id, v): @app.post("/unsticky/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def unsticky_post(post_id, v): post = get_post(post_id) @@ -1382,6 +1422,7 @@ def unsticky_post(post_id, v): return {"message": "Post unpinned!"} @app.post("/sticky_comment/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def sticky_comment(cid, v): comment = get_comment(cid, v=v) @@ -1411,6 +1452,7 @@ def sticky_comment(cid, v): @app.post("/unsticky_comment/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def unsticky_comment(cid, v): comment = get_comment(cid, v=v) @@ -1442,6 +1484,7 @@ def unsticky_comment(cid, v): @app.post("/remove_comment/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def remove_comment(c_id, v): comment = get_comment(c_id) @@ -1462,6 +1505,7 @@ def remove_comment(c_id, v): @app.post("/approve_comment/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def approve_comment(c_id, v): comment = get_comment(c_id) @@ -1487,6 +1531,7 @@ def approve_comment(c_id, v): @app.post("/distinguish_comment/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_DISTINGUISH']) def admin_distinguish_comment(c_id, v): comment = get_comment(c_id, v=v) @@ -1512,6 +1557,7 @@ def admin_distinguish_comment(c_id, v): else: return {"message": "Comment undistinguished!"} @app.get("/admin/banned_domains/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['DOMAINS_BAN']) def admin_banned_domains(v): banned_domains = g.db.query(BannedDomain) \ @@ -1521,6 +1567,7 @@ def admin_banned_domains(v): @app.post("/admin/ban_domain") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['DOMAINS_BAN']) def ban_domain(v): @@ -1552,6 +1599,7 @@ def ban_domain(v): @app.post("/admin/unban_domain/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['DOMAINS_BAN']) def unban_domain(v:User, domain): existing = g.db.get(BannedDomain, domain) @@ -1571,6 +1619,7 @@ def unban_domain(v:User, domain): @app.post("/admin/nuke_user") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def admin_nuke_user(v): @@ -1604,6 +1653,7 @@ def admin_nuke_user(v): @app.post("/admin/unnuke_user") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def admin_nunuke_user(v): diff --git a/files/routes/asset_submissions.py b/files/routes/asset_submissions.py index 82d6af26c..e57ca2a5b 100644 --- a/files/routes/asset_submissions.py +++ b/files/routes/asset_submissions.py @@ -17,6 +17,7 @@ CAN_APPROVE_ASSETS = (AEVANN_ID, CARP_ID) CAN_UPDATE_ASSETS = (AEVANN_ID, CARP_ID) @app.get("/submit/marseys") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def submit_marseys(v:User): if v.admin_level >= PERMS['VIEW_PENDING_SUBMITTED_MARSEYS']: @@ -34,6 +35,7 @@ def submit_marseys(v:User): @app.post("/submit/marseys") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def submit_marsey(v:User): file = request.files["image"] @@ -107,6 +109,7 @@ def verify_permissions_and_get_asset(cls, asset_type:str, v:User, name:str, make return asset @app.post("/admin/approve/marsey/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['MODERATE_PENDING_SUBMITTED_MARSEYS']) def approve_marsey(v, name): marsey = verify_permissions_and_get_asset(Marsey, "marsey", v, name, True) @@ -189,11 +192,13 @@ def remove_asset(cls, type_name:str, v:User, name:str) -> dict[str, str]: return {"message": f"'{name}' removed!"} @app.post("/remove/marsey/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def remove_marsey(v:User, name): return remove_asset(Marsey, "marsey", v, name) @app.get("/submit/hats") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def submit_hats(v:User): if v.admin_level >= PERMS['VIEW_PENDING_SUBMITTED_HATS']: hats = g.db.query(HatDef).filter(HatDef.submitter_id != None) @@ -203,6 +208,7 @@ def submit_hats(v:User): @app.post("/submit/hats") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def submit_hat(v:User): name = request.values.get('name', '').strip() @@ -264,6 +270,7 @@ def submit_hat(v:User): @app.post("/admin/approve/hat/") @limiter.limit("3/second;120/minute;200/hour;1000/day") +@limiter.limit("3/second;120/minute;200/hour;1000/day", key_func=get_ID) @admin_level_required(PERMS['MODERATE_PENDING_SUBMITTED_HATS']) def approve_hat(v, name): hat = verify_permissions_and_get_asset(HatDef, "hat", v, name, False) @@ -326,11 +333,13 @@ def approve_hat(v, name): return {"message": f"'{hat.name}' approved!"} @app.post("/remove/hat/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def remove_hat(v:User, name): return remove_asset(HatDef, 'hat', v, name) @app.get("/admin/update/marseys") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['UPDATE_MARSEYS']) def update_marseys(v): if AEVANN_ID and v.id not in CAN_UPDATE_ASSETS: @@ -350,6 +359,7 @@ def update_marseys(v): @app.post("/admin/update/marseys") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['UPDATE_MARSEYS']) def update_marsey(v): if AEVANN_ID and v.id not in CAN_UPDATE_ASSETS: @@ -403,6 +413,7 @@ def update_marsey(v): return render_template("update_assets.html", v=v, msg=f"'{name}' updated successfully!", name=name, tags=tags, type="Marsey") @app.get("/admin/update/hats") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['UPDATE_HATS']) def update_hats(v): if AEVANN_ID and v.id not in CAN_UPDATE_ASSETS: @@ -411,6 +422,7 @@ def update_hats(v): @app.post("/admin/update/hats") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['UPDATE_HATS']) def update_hat(v): if AEVANN_ID and v.id not in CAN_UPDATE_ASSETS: diff --git a/files/routes/awards.py b/files/routes/awards.py index 6cee09d2b..e6cdd4587 100644 --- a/files/routes/awards.py +++ b/files/routes/awards.py @@ -22,6 +22,7 @@ from .front import frontlist @app.get("/shop") @app.get("/settings/shop") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def shop(v:User): AWARDS = deepcopy(AWARDS_ENABLED) @@ -46,6 +47,7 @@ def shop(v:User): @app.post("/buy/") @limiter.limit("100/minute;200/hour;1000/day") +@limiter.limit("100/minute;200/hour;1000/day", key_func=get_ID) @auth_required def buy(v:User, award): if award == 'benefactor' and not request.values.get("mb"): @@ -126,7 +128,7 @@ def buy(v:User, award): @app.post("/award//") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @is_not_permabanned def award_thing(v, thing_type, id): kind = request.values.get("kind", "").strip() diff --git a/files/routes/casino.py b/files/routes/casino.py index f510a4e46..3888c91e2 100644 --- a/files/routes/casino.py +++ b/files/routes/casino.py @@ -13,6 +13,7 @@ from files.__main__ import app, limiter @app.get("/casino") @limiter.limit("100/minute;2000/hour;12000/day") +@limiter.limit("100/minute;2000/hour;12000/day", key_func=get_ID) @auth_required def casino(v:User): if v.rehab: @@ -23,6 +24,7 @@ def casino(v:User): @app.get("/casino/") @limiter.limit("100/minute;2000/hour;12000/day") +@limiter.limit("100/minute;2000/hour;12000/day", key_func=get_ID) @auth_required def casino_game_page(v:User, game): if v.rehab: @@ -52,6 +54,7 @@ def casino_game_page(v:User, game): @app.get("/casino//feed") @limiter.limit("100/minute;2000/hour;12000/day") +@limiter.limit("100/minute;2000/hour;12000/day", key_func=get_ID) @auth_required def casino_game_feed(v:User, game): if v.rehab: @@ -66,6 +69,7 @@ def casino_game_feed(v:User, game): # Lottershe @app.get("/lottershe") @limiter.limit("100/minute;2000/hour;12000/day") +@limiter.limit("100/minute;2000/hour;12000/day", key_func=get_ID) @auth_required def lottershe(v:User): if v.rehab: @@ -77,6 +81,7 @@ def lottershe(v:User): # Slots @app.post("/casino/slots") @limiter.limit("100/minute;2000/hour;12000/day") +@limiter.limit("100/minute;2000/hour;12000/day", key_func=get_ID) @auth_required def pull_slots(v:User): if v.rehab: @@ -108,6 +113,7 @@ def pull_slots(v:User): # 21 @app.post("/casino/twentyone/deal") @limiter.limit("1/second;100/minute;2000/hour;12000/day") +@limiter.limit("1/second;100/minute;2000/hour;12000/day", key_func=get_ID) @auth_required def blackjack_deal_to_player(v:User): if v.rehab: @@ -127,6 +133,7 @@ def blackjack_deal_to_player(v:User): @app.post("/casino/twentyone/hit") @limiter.limit("1/second;100/minute;2000/hour;12000/day") +@limiter.limit("1/second;100/minute;2000/hour;12000/day", key_func=get_ID) @auth_required def blackjack_player_hit(v:User): if v.rehab: @@ -142,6 +149,7 @@ def blackjack_player_hit(v:User): @app.post("/casino/twentyone/stay") @limiter.limit("1/second;100/minute;2000/hour;12000/day") +@limiter.limit("1/second;100/minute;2000/hour;12000/day", key_func=get_ID) @auth_required def blackjack_player_stay(v:User): if v.rehab: @@ -157,6 +165,7 @@ def blackjack_player_stay(v:User): @app.post("/casino/twentyone/double-down") @limiter.limit("1/second;100/minute;2000/hour;12000/day") +@limiter.limit("1/second;100/minute;2000/hour;12000/day", key_func=get_ID) @auth_required def blackjack_player_doubled_down(v:User): if v.rehab: @@ -172,6 +181,7 @@ def blackjack_player_doubled_down(v:User): @app.post("/casino/twentyone/buy-insurance") @limiter.limit("1/second;100/minute;2000/hour;12000/day") +@limiter.limit("1/second;100/minute;2000/hour;12000/day", key_func=get_ID) @auth_required def blackjack_player_bought_insurance(v:User): if v.rehab: @@ -187,6 +197,7 @@ def blackjack_player_bought_insurance(v:User): # Roulette @app.get("/casino/roulette/bets") @limiter.limit("100/minute;2000/hour;12000/day") +@limiter.limit("100/minute;2000/hour;12000/day", key_func=get_ID) @auth_required def roulette_get_bets(v:User): if v.rehab: @@ -199,6 +210,7 @@ def roulette_get_bets(v:User): @app.post("/casino/roulette/place-bet") @limiter.limit("100/minute;2000/hour;12000/day") +@limiter.limit("100/minute;2000/hour;12000/day", key_func=get_ID) @auth_required def roulette_player_placed_bet(v:User): if v.rehab: diff --git a/files/routes/chat.py b/files/routes/chat.py index ae4f2f34f..10b8f419c 100644 --- a/files/routes/chat.py +++ b/files/routes/chat.py @@ -49,6 +49,7 @@ socket_ids_to_user_ids = {} user_ids_to_socket_ids = {} @app.get("/chat") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['CHAT']) def chat(v): if not v.admin_level and TRUESCORE_CHAT_MINIMUM and v.truescore < TRUESCORE_CHAT_MINIMUM: @@ -62,7 +63,7 @@ def admin_chat(v): @socketio.on('speak') @limiter.limit("3/second;10/minute") -@limiter.limit("3/second;10/minute", key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit("3/second;10/minute", key_func=get_ID) @admin_level_required(PERMS['CHAT']) def speak(data, v): limiter.check() @@ -138,6 +139,7 @@ def refresh_online(): cache.set(CHAT_ONLINE_CACHE_KEY, len(online[request.referrer]), timeout=0) @socketio.on('connect') +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['CHAT']) def connect(v): join_room(request.referrer) @@ -155,6 +157,7 @@ def connect(v): return '', 204 @socketio.on('disconnect') +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['CHAT']) def disconnect(v): if v.username in online[request.referrer]: @@ -174,6 +177,7 @@ def disconnect(v): return '', 204 @socketio.on('typing') +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['CHAT']) def typing_indicator(data, v): @@ -187,6 +191,7 @@ def typing_indicator(data, v): @socketio.on('delete') +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['POST_COMMENT_MODERATION']) def delete(text, v): diff --git a/files/routes/comments.py b/files/routes/comments.py index 7e04a9660..64e20b3bd 100644 --- a/files/routes/comments.py +++ b/files/routes/comments.py @@ -83,7 +83,7 @@ def post_pid_comment_cid(cid, pid=None, anything=None, v=None, sub=None): @app.post("/comment") @limiter.limit("1/second;20/minute;200/hour;1000/day") -@limiter.limit("1/second;20/minute;200/hour;1000/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit("1/second;20/minute;200/hour;1000/day", key_func=get_ID) @auth_required def comment(v:User): if v.is_suspended: abort(403, "You can't perform this action while banned.") @@ -367,7 +367,7 @@ def comment(v:User): @app.post("/edit_comment/") @limiter.limit("1/second;10/minute;100/hour;200/day") -@limiter.limit("1/second;10/minute;100/hour;200/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit("1/second;10/minute;100/hour;200/day", key_func=get_ID) @is_not_permabanned def edit_comment(cid, v): c = get_comment(cid, v=v) @@ -442,7 +442,7 @@ def edit_comment(cid, v): @app.post("/delete/comment/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def delete_comment(cid, v): if v.id == 253: abort(403) @@ -464,7 +464,7 @@ def delete_comment(cid, v): @app.post("/undelete/comment/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def undelete_comment(cid, v): c = get_comment(cid, v=v) @@ -484,6 +484,7 @@ def undelete_comment(cid, v): @app.post("/pin_comment/") @feature_required('PINS') +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def pin_comment(cid, v): @@ -506,6 +507,7 @@ def pin_comment(cid, v): @app.post("/unpin_comment/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def unpin_comment(cid, v): @@ -528,7 +530,7 @@ def unpin_comment(cid, v): @app.post("/save_comment/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def save_comment(cid, v): @@ -545,7 +547,7 @@ def save_comment(cid, v): @app.post("/unsave_comment/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def unsave_comment(cid, v): @@ -581,7 +583,7 @@ def diff_words(answer, guess): @app.post("/wordle/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def handle_wordle_action(cid, v): comment = get_comment(cid) @@ -612,6 +614,7 @@ def handle_wordle_action(cid, v): @app.post("/toggle_comment_nsfw/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def toggle_comment_nsfw(cid, v): comment = get_comment(cid) diff --git a/files/routes/front.py b/files/routes/front.py index 4e486f580..ccede8084 100644 --- a/files/routes/front.py +++ b/files/routes/front.py @@ -154,6 +154,7 @@ def frontlist(v=None, sort="hot", page=1, t="all", ids_only=True, filter_words=' @app.get("/random_post") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def random_post(v:User): @@ -166,6 +167,7 @@ def random_post(v:User): @app.get("/random_user") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def random_user(v:User): u = g.db.query(User.username).filter(User.song != None, User.shadowbanned == None).order_by(func.random()).first() @@ -204,6 +206,7 @@ def comment_idlist(v=None, page=1, sort="new", t="day", gt=0, lt=0, site=None): return [x[0] for x in comments] @app.get("/comments") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def all_comments(v:User): if SITE == 'rdrama.net': diff --git a/files/routes/giphy.py b/files/routes/giphy.py index 816b30a0e..2e39c6a1c 100644 --- a/files/routes/giphy.py +++ b/files/routes/giphy.py @@ -7,6 +7,7 @@ from files.__main__ import app @app.get("/giphy") @app.get("/giphy") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def giphy(v=None, path=None): diff --git a/files/routes/hats.py b/files/routes/hats.py index c8eaf2d7c..106701a21 100644 --- a/files/routes/hats.py +++ b/files/routes/hats.py @@ -8,6 +8,7 @@ from files.routes.wrappers import * from files.__main__ import app, limiter @app.get("/hats") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def hats(v:User): owned_hat_ids = [x.hat_id for x in v.owned_hats] @@ -28,6 +29,7 @@ def hats(v:User): @app.post("/buy_hat/") @limiter.limit('100/minute;1000/3 days') +@limiter.limit('100/minute;1000/3 days', key_func=get_ID) @auth_required def buy_hat(v:User, hat_id): try: hat_id = int(hat_id) @@ -78,6 +80,7 @@ def buy_hat(v:User, hat_id): @app.post("/equip_hat/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def equip_hat(v:User, hat_id): try: hat_id = int(hat_id) @@ -92,6 +95,7 @@ def equip_hat(v:User, hat_id): return {"message": f"'{hat.name}' equipped!"} @app.post("/unequip_hat/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def unequip_hat(v:User, hat_id): try: hat_id = int(hat_id) @@ -106,6 +110,7 @@ def unequip_hat(v:User, hat_id): return {"message": f"'{hat.name}' unequipped!"} @app.get("/hat_owners/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def hat_owners(v:User, hat_id): try: hat_id = int(hat_id) diff --git a/files/routes/login.py b/files/routes/login.py index f1a88c409..dac433f6e 100644 --- a/files/routes/login.py +++ b/files/routes/login.py @@ -128,6 +128,7 @@ def on_login(account, redir=None): @app.get("/me") @app.get("/@me") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def me(v:User): if v.client: return v.json @@ -136,7 +137,7 @@ def me(v:User): @app.post("/logout") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def logout(v): loggedin = cache.get(f'{SITE}_loggedin') or {} diff --git a/files/routes/lottery.py b/files/routes/lottery.py index 5cdae2b2d..c53bb7edc 100644 --- a/files/routes/lottery.py +++ b/files/routes/lottery.py @@ -7,6 +7,7 @@ from files.routes.wrappers import * from files.__main__ import app, limiter @app.post("/lottery/end") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['LOTTERY_ADMIN']) def lottery_end(v): success, message = end_lottery_session() @@ -14,6 +15,7 @@ def lottery_end(v): @app.post("/lottery/start") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['LOTTERY_ADMIN']) def lottery_start(v): start_new_lottery_session() @@ -22,6 +24,7 @@ def lottery_start(v): @app.post("/lottery/buy") @limiter.limit("3/second;100/minute;500/hour;1000/day") +@limiter.limit("3/second;100/minute;500/hour;1000/day", key_func=get_ID) @auth_required def lottery_buy(v:User): try: quantity = int(request.values.get("quantity")) @@ -39,6 +42,7 @@ def lottery_buy(v:User): @app.get("/lottery/active") @limiter.limit("3/second;100/minute;500/hour;1000/day") +@limiter.limit("3/second;100/minute;500/hour;1000/day", key_func=get_ID) @auth_required def lottery_active(v:User): lottery, participants = get_active_lottery_stats() @@ -46,6 +50,7 @@ def lottery_active(v:User): return {"message": "", "stats": {"user": v.lottery_stats, "lottery": lottery, "participants": participants}} @app.get("/admin/lottery/participants") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['LOTTERY_VIEW_PARTICIPANTS']) def lottery_admin(v): participants = get_users_participating_in_lottery() diff --git a/files/routes/mail.py b/files/routes/mail.py index 20131df5c..77c5fcaa1 100644 --- a/files/routes/mail.py +++ b/files/routes/mail.py @@ -10,7 +10,7 @@ from files.__main__ import app, limiter @app.post("/verify_email") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def verify_email(v): send_verification_email(v) @@ -18,6 +18,7 @@ def verify_email(v): @app.get("/activate") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def activate(v:User): email = request.values.get("email", "").strip().lower() diff --git a/files/routes/notifications.py b/files/routes/notifications.py index 7323b443c..f333e5767 100644 --- a/files/routes/notifications.py +++ b/files/routes/notifications.py @@ -10,7 +10,7 @@ from files.routes.wrappers import * from files.__main__ import app @app.post("/clear") -@limiter.limit(DEFAULT_RATELIMIT, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def clear(v): notifs = g.db.query(Notification).join(Notification.comment).filter(Notification.read == False, Notification.user_id == v.id).all() @@ -24,7 +24,7 @@ def clear(v): @app.get("/unread") -@limiter.limit(DEFAULT_RATELIMIT, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def unread(v): listing = g.db.query(Notification, Comment).join(Notification.comment).filter( @@ -42,6 +42,7 @@ def unread(v): @app.get("/notifications/modmail") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['VIEW_MODMAIL']) def notifications_modmail(v): try: page = max(int(request.values.get("page", 1)), 1) @@ -67,6 +68,7 @@ def notifications_modmail(v): @app.get("/notifications/messages") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def notifications_messages(v:User): try: page = max(int(request.values.get("page", 1)), 1) @@ -136,6 +138,7 @@ def notifications_messages(v:User): @app.get("/notifications/posts") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def notifications_posts(v:User): try: page = max(int(request.values.get("page", 1)), 1) @@ -178,6 +181,7 @@ def notifications_posts(v:User): @app.get("/notifications/modactions") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def notifications_modactions(v:User): try: page = max(int(request.values.get("page", 1)), 1) @@ -220,6 +224,7 @@ def notifications_modactions(v:User): @app.get("/notifications/reddit") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def notifications_reddit(v:User): try: page = max(int(request.values.get("page", 1)), 1) @@ -257,6 +262,7 @@ def notifications_reddit(v:User): @app.get("/notifications") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def notifications(v:User): try: page = max(int(request.values.get("page", 1)), 1) diff --git a/files/routes/oauth.py b/files/routes/oauth.py index 85d5b2f73..4ace0b89b 100644 --- a/files/routes/oauth.py +++ b/files/routes/oauth.py @@ -8,6 +8,7 @@ from files.routes.wrappers import * from files.__main__ import app, limiter @app.get("/authorize") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def authorize_prompt(v:User): client_id = request.values.get("client_id") @@ -17,7 +18,7 @@ def authorize_prompt(v:User): @app.post("/authorize") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def authorize(v): client_id = request.values.get("client_id") @@ -37,7 +38,7 @@ def authorize(v): @app.post("/rescind/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def rescind(v, aid): @@ -49,7 +50,7 @@ def rescind(v, aid): @app.post("/api_keys") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @is_not_permabanned def request_api_keys(v): new_app = OauthApp( @@ -88,7 +89,7 @@ def request_api_keys(v): @app.post("/delete_app/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def delete_oauth_app(v, aid): try: @@ -111,7 +112,7 @@ def delete_oauth_app(v, aid): @app.post("/edit_app/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @is_not_permabanned def edit_oauth_app(v, aid): try: @@ -135,6 +136,7 @@ def edit_oauth_app(v, aid): @app.post("/admin/app/approve/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['APPS_MODERATION']) def admin_app_approve(v, aid): @@ -171,6 +173,7 @@ def admin_app_approve(v, aid): @app.post("/admin/app/revoke/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['APPS_MODERATION']) def admin_app_revoke(v, aid): @@ -196,6 +199,7 @@ def admin_app_revoke(v, aid): @app.post("/admin/app/reject/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @admin_level_required(PERMS['APPS_MODERATION']) def admin_app_reject(v, aid): @@ -221,6 +225,7 @@ def admin_app_reject(v, aid): @app.get("/admin/app//posts") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['APPS_MODERATION']) def admin_app_id_posts(v, aid): aid=aid @@ -242,6 +247,7 @@ def admin_app_id_posts(v, aid): ) @app.get("/admin/app//comments") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['APPS_MODERATION']) def admin_app_id_comments(v, aid): @@ -268,6 +274,7 @@ def admin_app_id_comments(v, aid): @app.get("/admin/apps") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['APPS_MODERATION']) def admin_apps_list(v): @@ -278,7 +285,7 @@ def admin_apps_list(v): @app.post("/reroll/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def reroll_oauth_tokens(aid, v): diff --git a/files/routes/polls.py b/files/routes/polls.py index bcead7b44..78cd3e8c5 100644 --- a/files/routes/polls.py +++ b/files/routes/polls.py @@ -6,6 +6,7 @@ from files.__main__ import app @app.post("/vote/post/option/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def vote_option(option_id, v): try: @@ -52,6 +53,7 @@ def vote_option(option_id, v): return {"message": "Bet successful!"} @app.get("/votes/post/option/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def option_votes(option_id, v): try: @@ -83,6 +85,7 @@ def option_votes(option_id, v): @app.post("/vote/comment/option/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def vote_option_comment(option_id, v): try: @@ -117,6 +120,7 @@ def vote_option_comment(option_id, v): return "", 204 @app.get("/votes/comment/option/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def option_votes_comment(option_id, v): try: diff --git a/files/routes/posts.py b/files/routes/posts.py index 82d0dacf4..ff2769411 100644 --- a/files/routes/posts.py +++ b/files/routes/posts.py @@ -34,7 +34,7 @@ titleheaders = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWe @app.post("/publish/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def publish(pid, v): post = get_post(pid) @@ -66,6 +66,7 @@ def publish(pid, v): @app.get("/submit") @app.get("/h//submit") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def submit_get(v:User, sub=None): sub = get_sub_by_name(sub, graceful=True) @@ -253,7 +254,7 @@ def more_comments(v, cid): @app.post("/edit_post/") @limiter.limit("1/second;10/minute;100/hour;200/day") -@limiter.limit("1/second;10/minute;100/hour;200/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit("1/second;10/minute;100/hour;200/day", key_func=get_ID) @is_not_permabanned def edit_post(pid, v): p = get_post(pid) @@ -519,7 +520,7 @@ def is_repost(): @app.post("/submit") @app.post("/h//submit") @limiter.limit(POST_RATE_LIMIT) -@limiter.limit(POST_RATE_LIMIT, key_func=lambda:f'{SITE}-{session.get("lo_user")}') +@limiter.limit(POST_RATE_LIMIT, key_func=get_ID) @auth_required def submit_post(v:User, sub=None): @@ -822,7 +823,7 @@ def submit_post(v:User, sub=None): @app.post("/delete_post/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def delete_post_pid(pid, v): post = get_post(pid) @@ -849,7 +850,7 @@ def delete_post_pid(pid, v): @app.post("/undelete_post/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def undelete_post_pid(pid, v): post = get_post(pid) @@ -870,6 +871,7 @@ def undelete_post_pid(pid, v): @app.post("/mark_post_nsfw/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def mark_post_nsfw(pid, v): post = get_post(pid) @@ -904,6 +906,7 @@ def mark_post_nsfw(pid, v): return {"message": "Post has been marked as +18!"} @app.post("/unmark_post_nsfw/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def unmark_post_nsfw(pid, v): post = get_post(pid) @@ -939,7 +942,7 @@ def unmark_post_nsfw(pid, v): @app.post("/save_post/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def save_post(pid, v): @@ -955,7 +958,7 @@ def save_post(pid, v): @app.post("/unsave_post/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def unsave_post(pid, v): @@ -969,6 +972,7 @@ def unsave_post(pid, v): return {"message": "Post unsaved!"} @app.post("/pin/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def pin_post(post_id, v): post = get_post(post_id) @@ -983,6 +987,7 @@ def pin_post(post_id, v): @app.put("/post//new") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def set_new_sort(post_id:int, v:User): post = get_post(post_id) @@ -1004,6 +1009,7 @@ def set_new_sort(post_id:int, v:User): @app.delete("/post//new") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def unset_new_sort(post_id:int, v:User): post = get_post(post_id) @@ -1027,7 +1033,7 @@ extensions = IMAGE_FORMATS + VIDEO_FORMATS + AUDIO_FORMATS @app.get("/submit/title") @limiter.limit("3/minute") -@limiter.limit("3/minute", key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit("3/minute", key_func=get_ID) @auth_required def get_post_title(v): POST_TITLE_TIMEOUT = 5 diff --git a/files/routes/push_notifs.py b/files/routes/push_notifs.py index bfb986103..f248d05cf 100644 --- a/files/routes/push_notifs.py +++ b/files/routes/push_notifs.py @@ -4,6 +4,7 @@ from flask import request, g from files.classes.push_subscriptions import PushSubscription @app.post("/push_subscribe") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def push_subscribe(v): subscription_json = request.values.get("subscription_json") diff --git a/files/routes/reporting.py b/files/routes/reporting.py index 26baba8f9..365a7a9d8 100644 --- a/files/routes/reporting.py +++ b/files/routes/reporting.py @@ -13,7 +13,7 @@ from files.__main__ import app, limiter, cache @app.post("/report/post/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def flag_post(pid, v): post = get_post(pid) @@ -69,7 +69,7 @@ def flag_post(pid, v): @app.post("/report/comment/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def flag_comment(cid, v): @@ -95,6 +95,7 @@ def flag_comment(cid, v): @app.post('/del_report/post//') @limiter.limit("4/second;100/minute;300/hour;2000/day") +@limiter.limit("4/second;100/minute;300/hour;2000/day", key_func=get_ID) @admin_level_required(PERMS['FLAGS_REMOVE']) def remove_report_post(v, pid, uid): try: @@ -118,6 +119,7 @@ def remove_report_post(v, pid, uid): @app.post('/del_report/comment//') @limiter.limit("4/second;100/minute;300/hour;2000/day") +@limiter.limit("4/second;100/minute;300/hour;2000/day", key_func=get_ID) @admin_level_required(PERMS['FLAGS_REMOVE']) def remove_report_comment(v, cid, uid): try: diff --git a/files/routes/search.py b/files/routes/search.py index 4b4f34fb7..9888d212f 100644 --- a/files/routes/search.py +++ b/files/routes/search.py @@ -43,6 +43,7 @@ def searchparse(text): return criteria @app.get("/search/posts") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def searchposts(v:User): @@ -169,6 +170,7 @@ def searchposts(v:User): ) @app.get("/search/comments") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def searchcomments(v:User): query = request.values.get("q", '').strip() @@ -270,6 +272,7 @@ def searchcomments(v:User): @app.get("/search/users") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def searchusers(v:User): diff --git a/files/routes/settings.py b/files/routes/settings.py index 2a227d759..819b9685d 100644 --- a/files/routes/settings.py +++ b/files/routes/settings.py @@ -25,18 +25,20 @@ from files.__main__ import app, cache, limiter @app.get("/settings") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def settings(v:User): return redirect("/settings/personal") @app.get("/settings/personal") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def settings_personal(v:User): return render_template("settings/personal.html", v=v) @app.delete('/settings/background') @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def remove_background(v): if v.background: @@ -48,7 +50,7 @@ def remove_background(v): @app.post('/settings/custom_background') @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def upload_custom_background(v): if g.is_tor: abort(403, "Image uploads are not allowed through TOR.") @@ -72,7 +74,7 @@ def upload_custom_background(v): @app.post('/settings/profile_background') @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def upload_profile_background(v): if g.is_tor: abort(403, "Image uploads are not allowed through TOR.") @@ -93,7 +95,7 @@ def upload_profile_background(v): @app.delete('/settings/profile_background') @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def delete_profile_background(v): if v.profile_background: @@ -103,7 +105,7 @@ def delete_profile_background(v): @app.post("/settings/personal") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def settings_personal_post(v): if v.id == 253 and request.values.get("private"): @@ -358,6 +360,7 @@ def settings_personal_post(v): @app.post("/settings/filters") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def filters(v:User): filters=request.values.get("filters")[:1000].strip() @@ -385,21 +388,21 @@ def set_color(v:User, attr:str, color:Optional[str]): @app.post("/settings/namecolor") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def namecolor(v): return set_color(v, "namecolor", request.values.get("namecolor")) @app.post("/settings/themecolor") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def themecolor(v): return set_color(v, "themecolor", request.values.get("themecolor")) @app.post("/settings/gumroad") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def gumroad(v): if GUMROAD_TOKEN == DEFAULT_CONFIG_VALUE: abort(404) @@ -436,14 +439,14 @@ def gumroad(v): @app.post("/settings/titlecolor") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def titlecolor(v): return set_color(v, "titlecolor", request.values.get("titlecolor")) @app.post("/settings/verifiedcolor") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def verifiedcolor(v): if not v.verified: abort(403, "You don't have a checkmark") @@ -451,7 +454,7 @@ def verifiedcolor(v): @app.post("/settings/security") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def settings_security_post(v): if request.values.get("new_password"): @@ -525,7 +528,7 @@ def settings_security_post(v): @app.post("/settings/log_out_all_others") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def settings_log_out_others(v): submitted_password = request.values.get("password", "").strip() @@ -540,7 +543,7 @@ def settings_log_out_others(v): @app.post("/settings/images/profile") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def settings_images_profile(v): if g.is_tor: abort(403, "Image uploads are not allowed through TOR.") @@ -575,7 +578,7 @@ def settings_images_profile(v): @app.post("/settings/images/banner") @feature_required('USERS_PROFILE_BANNER') @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def settings_images_banner(v): if g.is_tor: abort(403, "Image uploads are not allowed through TOR.") @@ -595,13 +598,14 @@ def settings_images_banner(v): return render_template("settings/personal.html", v=v, msg="Banner successfully updated.") @app.get("/settings/css") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def settings_css_get(v:User): return render_template("settings/css.html", v=v) @app.post("/settings/css") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def settings_css(v): if v.agendaposter: abort(400, "Agendapostered users can't edit CSS!") @@ -613,7 +617,7 @@ def settings_css(v): @app.post("/settings/profilecss") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def settings_profilecss(v): profilecss = request.values.get("profilecss", v.profilecss).strip().replace('\\', '').strip()[:CSS_LENGTH_LIMIT] @@ -625,6 +629,7 @@ def settings_profilecss(v): return render_template("settings/css.html", v=v, msg="Profile CSS successfully updated!") @app.get("/settings/security") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def settings_security(v:User): return render_template("settings/security.html", @@ -635,7 +640,7 @@ def settings_security(v:User): @app.post("/settings/block") @limiter.limit("1/second;20/day") -@limiter.limit("1/second;20/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit("1/second;20/day", key_func=get_ID) @auth_required def settings_block_user(v): user = get_user(request.values.get("username"), graceful=True) @@ -662,7 +667,7 @@ def settings_block_user(v): @app.post("/settings/unblock") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def settings_unblock_user(v): user = get_user(request.values.get("username")) @@ -675,18 +680,20 @@ def settings_unblock_user(v): return {"message": f"@{user.username} unblocked successfully!"} @app.get("/settings/apps") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def settings_apps(v:User): return render_template("settings/apps.html", v=v) @app.get("/settings/advanced") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def settings_advanced_get(v:User): return render_template("settings/advanced.html", v=v) @app.post("/settings/name_change") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @is_not_permabanned def settings_name_change(v): new_name=request.values.get("name").strip() @@ -725,7 +732,7 @@ def settings_name_change(v): @app.post("/settings/song_change_mp3") @feature_required('USERS_PROFILE_SONG') @limiter.limit("1/second;10/day") -@limiter.limit("1/second;10/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit("1/second;10/day", key_func=get_ID) @auth_required def settings_song_change_mp3(v): file = request.files['file'] @@ -753,7 +760,7 @@ def settings_song_change_mp3(v): @app.post("/settings/song_change") @feature_required('USERS_PROFILE_SONG') @limiter.limit("3/second;10/day") -@limiter.limit("3/second;10/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit("3/second;10/day", key_func=get_ID) @auth_required def settings_song_change(v): song=request.values.get("song").strip() @@ -831,7 +838,7 @@ def settings_song_change(v): @app.post("/settings/title_change") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def settings_title_change(v): if v.flairchanged: abort(403) @@ -860,7 +867,7 @@ def settings_title_change(v): @app.post("/settings/pronouns_change") @feature_required('PRONOUNS') @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def settings_pronouns_change(v): pronouns = sanitize_settings_text(request.values.get("pronouns")) @@ -886,7 +893,7 @@ def settings_pronouns_change(v): @app.post("/settings/checkmark_text") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def settings_checkmark_text(v): if not v.verified: abort(403) @@ -899,6 +906,7 @@ def settings_checkmark_text(v): if IS_FISTMAS(): @app.post("/events/fistmas2022/darkmode") + @limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def event_darkmode(v): v.event_darkmode = not v.event_darkmode diff --git a/files/routes/static.py b/files/routes/static.py index 8e27ae211..d3b9d7428 100644 --- a/files/routes/static.py +++ b/files/routes/static.py @@ -39,6 +39,7 @@ def reddit_post(subreddit, v, path): @app.get("/marseys") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def marseys(v:User): @@ -82,6 +83,7 @@ def sidebar(v:Optional[User]): @app.get("/stats") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def participation_stats(v:User): stats = cache.get(f'{SITE}_stats') or {} @@ -93,17 +95,20 @@ def chart(): return redirect('/weekly_chart') @app.get("/weekly_chart") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def weekly_chart(v:User): return send_file(statshelper.chart_path(kind="weekly", site=SITE)) @app.get("/daily_chart") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def daily_chart(v:User): return send_file(statshelper.chart_path(kind="daily", site=SITE)) @app.get("/patrons") @app.get("/paypigs") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['VIEW_PATRONS']) def patrons(v): if AEVANN_ID and v.id not in {AEVANN_ID, CARP_ID}: @@ -115,6 +120,7 @@ def patrons(v): @app.get("/admins") @app.get("/badmins") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def admins(v:User): admins = g.db.query(User).filter(User.admin_level >= PERMS['ADMIN_MOP_VISIBLE']).order_by(User.truescore.desc()).all() @@ -122,6 +128,7 @@ def admins(v:User): @app.get("/log") @app.get("/modlog") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def log(v:User): @@ -164,6 +171,7 @@ def log(v:User): return render_template("log.html", v=v, admins=admins, types=types, admin=admin, type=kind, actions=actions, next_exists=next_exists, page=page, single_user_url='admin') @app.get("/log/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def log_item(id, v): try: id = int(id) @@ -181,11 +189,13 @@ def log_item(id, v): return render_template("log.html", v=v, actions=[action], next_exists=False, page=1, action=action, admins=admins, types=types, single_user_url='admin') @app.get("/directory") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def static_megathread_index(v:User): return render_template("megathread_index.html", v=v) @app.get("/api") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def api(v): return render_template("api.html", v=v) @@ -201,7 +211,7 @@ def contact(v:Optional[User]): @app.post("/send_admin") @limiter.limit("1/second;1/2 minutes;10/day") -@limiter.limit("1/second;1/2 minutes;10/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit("1/second;1/2 minutes;10/day", key_func=get_ID) @auth_required def submit_contact(v): body = request.values.get("message") @@ -259,12 +269,14 @@ def badge_list(site): @app.get("/badges") @feature_required('BADGES') +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def badges(v:User): badges, counts = badge_list(SITE) return render_template("badges.html", v=v, badges=badges, counts=counts) @app.get("/blocks") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @admin_level_required(PERMS['USER_BLOCKS_VISIBLE']) def blocks(v): blocks=g.db.query(UserBlock).all() @@ -280,6 +292,7 @@ def blocks(v): return render_template("blocks.html", v=v, users=users, targets=targets) @app.get("/formatting") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def formatting(v:User): return render_template("formatting.html", v=v) @@ -295,6 +308,7 @@ def dismiss_mobile_tip(): return "", 204 @app.get("/transfers/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def transfers_id(id, v): @@ -308,6 +322,7 @@ def transfers_id(id, v): return render_template("transfers.html", v=v, page=1, comments=[transfer], standalone=True, next_exists=False) @app.get("/transfers") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def transfers(v:User): diff --git a/files/routes/subs.py b/files/routes/subs.py index f88384d50..a2c30351c 100644 --- a/files/routes/subs.py +++ b/files/routes/subs.py @@ -10,6 +10,7 @@ from .front import frontlist from files.__main__ import app, cache, limiter @app.post("/exile/post/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def exile_post(v:User, pid): if v.shadowbanned: abort(500) @@ -41,6 +42,7 @@ def exile_post(v:User, pid): return {"message": f"@{u.username} has been exiled from /h/{sub} successfully!"} @app.post("/exile/comment/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def exile_comment(v:User, cid): if v.shadowbanned: abort(500) @@ -72,6 +74,7 @@ def exile_comment(v:User, cid): return {"message": f"@{u.username} has been exiled from /h/{sub} successfully!"} @app.post("/h//unexile/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def unexile(v:User, sub, uid): u = get_account(uid) @@ -100,6 +103,7 @@ def unexile(v:User, sub, uid): return redirect(f'/h/{sub}/exilees') @app.post("/h//block") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def block_sub(v:User, sub): sub = get_sub_by_name(sub).name @@ -111,6 +115,7 @@ def block_sub(v:User, sub): return {"message": f"/h/{sub} blocked successfully!"} @app.post("/h//unblock") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def unblock_sub(v:User, sub): sub = get_sub_by_name(sub) @@ -126,6 +131,7 @@ def unblock_sub(v:User, sub): return {"message": f"/h/{sub.name} unblocked successfully!"} @app.post("/h//subscribe") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def subscribe_sub(v:User, sub): sub = get_sub_by_name(sub).name @@ -137,6 +143,7 @@ def subscribe_sub(v:User, sub): return {"message": f"/h/{sub} unblocked successfully!"} @app.post("/h//unsubscribe") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def unsubscribe_sub(v:User, sub): sub = get_sub_by_name(sub).name @@ -147,6 +154,7 @@ def unsubscribe_sub(v:User, sub): return {"message": f"/h/{sub} blocked successfully!"} @app.post("/h//follow") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def follow_sub(v:User, sub): sub = get_sub_by_name(sub) @@ -161,6 +169,7 @@ def follow_sub(v:User, sub): return {"message": f"/h/{sub} followed successfully!"} @app.post("/h//unfollow") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def unfollow_sub(v:User, sub): sub = get_sub_by_name(sub) @@ -172,6 +181,7 @@ def unfollow_sub(v:User, sub): return {"message": f"/h/{sub} unfollowed successfully!"} @app.get("/h//mods") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def mods(v:User, sub): sub = get_sub_by_name(sub) @@ -183,6 +193,7 @@ def mods(v:User, sub): @app.get("/h//exilees") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def sub_exilees(v:User, sub): sub = get_sub_by_name(sub) @@ -196,6 +207,7 @@ def sub_exilees(v:User, sub): @app.get("/h//blockers") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def sub_blockers(v:User, sub): sub = get_sub_by_name(sub) @@ -210,6 +222,7 @@ def sub_blockers(v:User, sub): @app.get("/h//followers") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def sub_followers(v:User, sub): sub = get_sub_by_name(sub) @@ -225,7 +238,7 @@ def sub_followers(v:User, sub): @app.post("/h//add_mod") @limiter.limit("1/second;30/day") -@limiter.limit("1/second;30/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit("1/second;30/day", key_func=get_ID) @is_not_permabanned def add_mod(v:User, sub): if SITE_NAME == 'WPD': abort(403) @@ -262,6 +275,7 @@ def add_mod(v:User, sub): return redirect(f'/h/{sub}/mods') @app.post("/h//remove_mod") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def remove_mod(v:User, sub): sub = get_sub_by_name(sub).name @@ -301,6 +315,7 @@ def remove_mod(v:User, sub): return {"message": f"@{user.username} has been removed as a mod!"} @app.get("/create_hole") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def create_sub(v): if not v.can_create_hole: @@ -309,6 +324,7 @@ def create_sub(v): return render_template("sub/create_hole.html", v=v, cost=HOLE_COST) @app.post("/create_hole") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def create_sub2(v): if not v.can_create_hole: @@ -342,6 +358,7 @@ def create_sub2(v): return redirect(f'/h/{sub}') @app.post("/kick/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def kick(v:User, pid): post = get_post(pid) @@ -373,6 +390,7 @@ def kick(v:User, pid): return {"message": f"Post kicked from /h/{old} successfully!"} @app.get('/h//settings') +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def sub_settings(v:User, sub): sub = get_sub_by_name(sub) @@ -382,7 +400,7 @@ def sub_settings(v:User, sub): @app.post('/h//sidebar') @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @is_not_permabanned def post_sub_sidebar(v:User, sub): sub = get_sub_by_name(sub) @@ -407,7 +425,7 @@ def post_sub_sidebar(v:User, sub): @app.post('/h//css') @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @is_not_permabanned def post_sub_css(v:User, sub): sub = get_sub_by_name(sub) @@ -447,7 +465,7 @@ def get_sub_css(sub): @app.post("/h//settings/banners/") @limiter.limit("1/second;50/day") -@limiter.limit("1/second;50/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit("1/second;50/day", key_func=get_ID) @is_not_permabanned def upload_sub_banner(v:User, sub:str): if g.is_tor: abort(403, "Image uploads are not allowed through Tor") @@ -477,7 +495,7 @@ def upload_sub_banner(v:User, sub:str): @app.delete("/h//settings/banners/") @limiter.limit("1/2 second;30/day") -@limiter.limit("1/2 second;30/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit("1/2 second;30/day", key_func=get_ID) @is_not_permabanned def delete_sub_banner(v:User, sub:str, index:int): sub = get_sub_by_name(sub) @@ -508,7 +526,7 @@ def delete_sub_banner(v:User, sub:str, index:int): @app.delete("/h//settings/banners/") @limiter.limit("1/10 second;30/day") -@limiter.limit("1/10 second;30/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit("1/10 second;30/day", key_func=get_ID) @is_not_permabanned def delete_all_sub_banners(v:User, sub:str): sub = get_sub_by_name(sub) @@ -534,7 +552,7 @@ def delete_all_sub_banners(v:User, sub:str): @app.post("/h//sidebar_image") @limiter.limit("1/second;10/day") -@limiter.limit("1/second;10/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit("1/second;10/day", key_func=get_ID) @is_not_permabanned def sub_sidebar(v:User, sub): if g.is_tor: abort(403, "Image uploads are not allowed through TOR.") @@ -565,7 +583,7 @@ def sub_sidebar(v:User, sub): @app.post("/h//marsey_image") @limiter.limit("1/second;10/day") -@limiter.limit("1/second;10/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit("1/second;10/day", key_func=get_ID) @is_not_permabanned def sub_marsey(v:User, sub): if g.is_tor: abort(403, "Image uploads are not allowed through TOR.") @@ -595,6 +613,7 @@ def sub_marsey(v:User, sub): return redirect(f'/h/{sub}/settings') @app.get("/holes") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def subs(v:User): subs = g.db.query(Sub, func.count(Submission.sub)).outerjoin(Submission, Sub.name == Submission.sub).group_by(Sub.name).order_by(func.count(Submission.sub).desc()).all() @@ -602,6 +621,7 @@ def subs(v:User): return render_template('sub/subs.html', v=v, subs=subs, total_users=total_users) @app.post("/hole_pin/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def hole_pin(v:User, pid): p = get_post(pid) @@ -630,6 +650,7 @@ def hole_pin(v:User, pid): return {"message": f"Post pinned to /h/{p.sub} successfully!"} @app.post("/hole_unpin/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def hole_unpin(v:User, pid): p = get_post(pid) @@ -659,6 +680,7 @@ def hole_unpin(v:User, pid): @app.post('/h//stealth') +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def sub_stealth(v:User, sub): sub = get_sub_by_name(sub) @@ -691,6 +713,7 @@ def sub_stealth(v:User, sub): @app.post("/mod_pin/") @feature_required('PINS') +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def mod_pin(cid, v): @@ -718,6 +741,7 @@ def mod_pin(cid, v): return {"message": "Comment pinned!"} @app.post("/unmod_pin/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @is_not_permabanned def mod_unpin(cid, v): @@ -745,6 +769,7 @@ def mod_unpin(cid, v): @app.get("/h//log") @app.get("/h//modlog") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def hole_log(v:User, sub): sub = get_sub_by_name(sub) @@ -786,6 +811,7 @@ def hole_log(v:User, sub): return render_template("log.html", v=v, admins=mods, types=types, admin=mod, type=kind, actions=actions, next_exists=next_exists, page=page, sub=sub, single_user_url='mod') @app.get("/h//log/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def hole_log_item(id, v, sub): sub = get_sub_by_name(sub) diff --git a/files/routes/users.py b/files/routes/users.py index 209757188..35f848d2a 100644 --- a/files/routes/users.py +++ b/files/routes/users.py @@ -56,24 +56,28 @@ def upvoters_downvoters(v, username, uid, cls, vote_cls, vote_dir, template, sta return render_template(template, next_exists=next_exists, listing=listing, page=page, v=v, standalone=standalone) @app.get("/@/upvoters//posts") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def upvoters_posts(v:User, username, uid): return upvoters_downvoters(v, username, uid, Submission, Vote, 1, "userpage/voted_posts.html", None) @app.get("/@/upvoters//comments") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def upvoters_comments(v:User, username, uid): return upvoters_downvoters(v, username, uid, Comment, CommentVote, 1, "userpage/voted_comments.html", True) @app.get("/@/downvoters//posts") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def downvoters_posts(v:User, username, uid): return upvoters_downvoters(v, username, uid, Submission, Vote, -1, "userpage/voted_posts.html", None) @app.get("/@/downvoters//comments") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def downvoters_comments(v:User, username, uid): return upvoters_downvoters(v, username, uid, Comment, CommentVote, -1, "userpage/voted_comments.html", True) @@ -107,24 +111,28 @@ def upvoting_downvoting(v, username, uid, cls, vote_cls, vote_dir, template, sta return render_template(template, next_exists=next_exists, listing=listing, page=page, v=v, standalone=standalone) @app.get("/@/upvoting//posts") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def upvoting_posts(v:User, username, uid): return upvoting_downvoting(v, username, uid, Submission, Vote, 1, "userpage/voted_posts.html", None) @app.get("/@/upvoting//comments") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def upvoting_comments(v:User, username, uid): return upvoting_downvoting(v, username, uid, Comment, CommentVote, 1, "userpage/voted_comments.html", True) @app.get("/@/downvoting//posts") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def downvoting_posts(v:User, username, uid): return upvoting_downvoting(v, username, uid, Submission, Vote, -1, "userpage/voted_posts.html", None) @app.get("/@/downvoting//comments") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def downvoting_comments(v:User, username, uid): return upvoting_downvoting(v, username, uid, Comment, CommentVote, -1, "userpage/voted_comments.html", True) @@ -158,17 +166,20 @@ def user_voted(v, username, cls, vote_cls, template, standalone): return render_template(template, next_exists=next_exists, listing=listing, page=page, v=v, standalone=standalone) @app.get("/@/voted/posts") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def user_voted_posts(v:User, username): return user_voted(v, username, Submission, Vote, "userpage/voted_posts.html", None) @app.get("/@/voted/comments") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def user_voted_comments(v:User, username): return user_voted(v, username, Comment, CommentVote, "userpage/voted_comments.html", True) @app.get("/banned") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def banned(v:User): users = g.db.query(User).filter( @@ -183,6 +194,7 @@ def banned(v:User): return render_template("banned.html", v=v, users=users) @app.get("/grassed") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def grassed(v:User): users = g.db.query(User).filter( @@ -195,6 +207,7 @@ def grassed(v:User): return render_template("grassed.html", v=v, users=users) @app.get("/chuds") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def chuds(v:User): users = g.db.query(User).filter( @@ -263,21 +276,25 @@ def all_upvoters_downvoters(v:User, username:str, vote_dir:int, is_who_simps_hat return render_template("userpage/voters.html", v=v, users=users, pos=pos, name=vote_name, name2=name2, total=total, page=page, next_exists=next_exists) @app.get("/@/upvoters") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def upvoters(v:User, username:str): return all_upvoters_downvoters(v, username, 1, False) @app.get("/@/downvoters") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def downvoters(v:User, username:str): return all_upvoters_downvoters(v, username, -1, False) @app.get("/@/upvoting") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def upvoting(v:User, username:str): return all_upvoters_downvoters(v, username, 1, True) @app.get("/@/downvoting") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def downvoting(v:User, username:str): return all_upvoters_downvoters(v, username, -1, True) @@ -285,7 +302,7 @@ def downvoting(v:User, username:str): @app.post("/@/suicide") @feature_required('USERS_SUICIDE') @limiter.limit("1/second;5/day") -@limiter.limit("1/second;5/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit("1/second;5/day", key_func=get_ID) @auth_required def suicide(v:User, username:str): user = get_user(username) @@ -296,6 +313,7 @@ def suicide(v:User, username:str): @app.get("/@/coins") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def get_coins(v:User, username:str): user = get_user(username, v=v, include_shadowbanned=False) @@ -343,7 +361,7 @@ def transfer_currency(v:User, username:str, currency_name:Literal['coins', 'mars @app.post("/@/transfer_coins") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @is_not_permabanned def transfer_coins(v:User, username:str): return transfer_currency(v, username, 'coins', True) @@ -351,12 +369,13 @@ def transfer_coins(v:User, username:str): @app.post("/@/transfer_bux") @feature_required('MARSEYBUX') @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @is_not_permabanned def transfer_bux(v:User, username:str): return transfer_currency(v, username, 'marseybux', False) @app.get("/leaderboard") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def leaderboard(v:User): users = g.db.query(User) @@ -437,7 +456,7 @@ def usersong(username:str): @app.post("/subscribe/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def subscribe(v, post_id): existing = g.db.query(Subscription).filter_by(user_id=v.id, submission_id=post_id).one_or_none() @@ -448,7 +467,7 @@ def subscribe(v, post_id): @app.post("/unsubscribe/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def unsubscribe(v, post_id): existing = g.db.query(Subscription).filter_by(user_id=v.id, submission_id=post_id).one_or_none() @@ -458,7 +477,7 @@ def unsubscribe(v, post_id): @app.post("/@/message") @limiter.limit("1/second;10/minute;20/hour;50/day") -@limiter.limit("1/second;10/minute;20/hour;50/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit("1/second;10/minute;20/hour;50/day", key_func=get_ID) @is_not_permabanned def message2(v:User, username:str): user = get_user(username, v=v, include_blocks=True, include_shadowbanned=False) @@ -519,7 +538,7 @@ def message2(v:User, username:str): @app.post("/reply") @limiter.limit("1/second;6/minute;50/hour;200/day") -@limiter.limit("1/second;6/minute;50/hour;200/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit("1/second;6/minute;50/hour;200/day", key_func=get_ID) @auth_required def messagereply(v:User): body = sanitize_raw_body(request.values.get("body"), False) @@ -604,6 +623,7 @@ def messagereply(v:User): return {"comment": render_template("comments.html", v=v, comments=[c])} @app.get("/2faqr/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def mfa_qr(v:User, secret:str): x = pyotp.TOTP(secret) @@ -623,6 +643,7 @@ def mfa_qr(v:User, secret:str): @app.get("/is_available/") @limiter.limit("100/day") +@limiter.limit("100/day", key_func=get_ID) def is_available(name:str): name=name.strip() @@ -650,11 +671,13 @@ def user_id(id): return redirect(user.url) @app.get("/u/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def redditor_moment_redirect(v:User, username:str): return redirect(f"/@{username}") @app.get("/@/followers") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def followers(v:User, username:str): u = get_user(username, v=v, include_shadowbanned=False) @@ -676,6 +699,7 @@ def followers(v:User, username:str): return render_template("userpage/followers.html", v=v, u=u, users=users, page=page, next_exists=next_exists) @app.get("/@/blockers") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def blockers(v:User, username:str): u = get_user(username, v=v, include_shadowbanned=False) @@ -694,6 +718,7 @@ def blockers(v:User, username:str): return render_template("userpage/blockers.html", v=v, u=u, users=users, page=page, next_exists=next_exists) @app.get("/@/following") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def following(v:User, username:str): u = get_user(username, v=v, include_shadowbanned=False) @@ -714,6 +739,7 @@ def following(v:User, username:str): return render_template("userpage/following.html", v=v, u=u, users=users, page=page, next_exists=next_exists) @app.get("/@/views") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def visitors(v:User, username:str): u = get_user(username, v=v, include_shadowbanned=False) @@ -995,6 +1021,7 @@ def u_username_comments(username, v=None): @app.get("/@/info") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def u_username_info(username, v=None): @@ -1008,6 +1035,7 @@ def u_username_info(username, v=None): return user.json @app.get("//info") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def u_user_id_info(id, v=None): @@ -1022,7 +1050,7 @@ def u_user_id_info(id, v=None): @app.post("/follow/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def follow_user(username, v): @@ -1049,7 +1077,7 @@ def follow_user(username, v): @app.post("/unfollow/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def unfollow_user(username, v): @@ -1077,7 +1105,7 @@ def unfollow_user(username, v): @app.post("/remove_follow/") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) -@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def remove_follow(username, v): target = get_user(username) @@ -1143,6 +1171,7 @@ def get_saves_and_subscribes(v, template, relationship_cls, page:int, standalone return render_template(template, u=v, v=v, listing=listing, page=page, next_exists=next_exists, standalone=standalone) @app.get("/@/saved/posts") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def saved_posts(v:User, username): try: page = max(1, int(request.values.get("page", 1))) @@ -1151,6 +1180,7 @@ def saved_posts(v:User, username): return get_saves_and_subscribes(v, "userpage/submissions.html", SaveRelationship, page, False) @app.get("/@/saved/comments") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def saved_comments(v:User, username): try: page = max(1, int(request.values.get("page", 1))) @@ -1159,6 +1189,7 @@ def saved_comments(v:User, username): return get_saves_and_subscribes(v, "userpage/comments.html", CommentSaveRelationship, page, True) @app.get("/@/subscribed/posts") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def subscribed_posts(v:User, username): try: page = max(1, int(request.values.get("page", 1))) @@ -1167,6 +1198,7 @@ def subscribed_posts(v:User, username): return get_saves_and_subscribes(v, "userpage/submissions.html", Subscription, page, False) @app.post("/fp/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def fp(v:User, fp): v.fp = fp @@ -1212,6 +1244,7 @@ def toggle_holes(): @app.get("/badge_owners/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def bid_list(v:User, bid): @@ -1274,6 +1307,7 @@ kofi_tiers={ @app.post("/settings/kofi") @limiter.limit(DEFAULT_RATELIMIT_SLOWER) +@limiter.limit(DEFAULT_RATELIMIT_SLOWER, key_func=get_ID) @auth_required def settings_kofi(v:User): if not KOFI_TOKEN: abort(404) @@ -1310,6 +1344,7 @@ def settings_kofi(v:User): return {"message": f"{patron} rewards claimed!"} @app.get("/users") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def users_list(v): diff --git a/files/routes/votes.py b/files/routes/votes.py index c8807e619..6997abccc 100644 --- a/files/routes/votes.py +++ b/files/routes/votes.py @@ -6,6 +6,7 @@ from files.__main__ import app, limiter from files.routes.routehelpers import get_alt_graph @app.get("/votes/") +@limiter.limit(DEFAULT_RATELIMIT, key_func=get_ID) @auth_required def vote_info_get(v, link): try: @@ -182,7 +183,7 @@ def vote_post_comment(target_id, new, v, cls, vote_cls): @app.post("/vote/post//") @limiter.limit("5/second;60/minute;1000/hour;2000/day") -@limiter.limit("5/second;60/minute;1000/hour;2000/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit("5/second;60/minute;1000/hour;2000/day", key_func=get_ID) @is_not_permabanned @limiter.limit("1/second", key_func=lambda:f'{g.v.id}-{request.full_path}') def vote_post(post_id, new, v): @@ -190,7 +191,7 @@ def vote_post(post_id, new, v): @app.post("/vote/comment//") @limiter.limit("5/second;60/minute;1000/hour;2000/day") -@limiter.limit("5/second;60/minute;1000/hour;2000/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}') +@limiter.limit("5/second;60/minute;1000/hour;2000/day", key_func=get_ID) @is_not_permabanned @limiter.limit("1/second", key_func=lambda:f'{g.v.id}-{request.full_path}') def vote_comment(comment_id, new, v): diff --git a/files/routes/wrappers.py b/files/routes/wrappers.py index 2ea9ea3cb..e3d2dc3e5 100644 --- a/files/routes/wrappers.py +++ b/files/routes/wrappers.py @@ -10,6 +10,16 @@ from files.helpers.settings import get_setting from files.routes.routehelpers import validate_formkey from files.__main__ import app, db_session, limiter +def get_ID(): + if request.headers.get("Authorization"): + x = request.headers.get("Authorization") + elif session.get("lo_user"): + x = session.get("lo_user") + else: + x = "logged_out" + + return f'{SITE}-{x}' + def get_logged_in_user(): if hasattr(g, 'v'): return g.v if not getattr(g, 'db', None): g.db = db_session()