diff --git a/app/activitypub/routes.py b/app/activitypub/routes.py index fa3dd3fe..dc86eeca 100644 --- a/app/activitypub/routes.py +++ b/app/activitypub/routes.py @@ -25,7 +25,7 @@ from app.activitypub.util import public_key, users_total, active_half_year, acti update_post_from_activity, undo_vote, undo_downvote, post_to_page, get_redis_connection, find_reported_object, \ process_report, ensure_domains_match, can_edit, can_delete, remove_data_from_banned_user, resolve_remote_post, \ inform_followers_of_post_update, comment_model_to_json, restore_post_or_comment, ban_local_user, unban_local_user, \ - lock_post, log_incoming_ap, find_community_ap_id + lock_post, log_incoming_ap, find_community_ap_id, site_ban_remove_data from app.utils import gibberish, get_setting, render_template, \ community_membership, ap_datetime, ip_address, can_downvote, \ can_upvote, can_create_post, awaken_dormant_instance, shorten_string, can_create_post_reply, sha256_digest, \ @@ -759,6 +759,36 @@ def process_inbox_request(request_json, store_ap_json): log_incoming_ap(request_json['id'], APLOG_REPORT, APLOG_IGNORED, request_json if store_ap_json else None, 'Report ignored due to missing content') return + if request_json['type'] == 'Block': # remote site is banning one of their users + blocker = user + blocked_ap_id = request_json['object'].lower() + blocked = User.query.filter_by(ap_profile_id=blocked_ap_id).first() + if store_ap_json: + request_json['cc'] = [] # cut very long list of instances + if not blocked: + log_incoming_ap(request_json['id'], APLOG_USERBAN, APLOG_IGNORED, request_json if store_ap_json else None, 'Does not exist here') + return + block_from_ap_id = request_json['target'] + remove_data = request_json['removeData'] if 'removeData' in request_json else False + + # Lemmy currently only sends userbans for admins banning local users + # Banning remote users is hacked by banning them from every community of which they are a part + # There's plans to change this in the future though. + if not blocker.is_instance_admin() and not blocked.instance_id == blocker.instance_id: + log_incoming_ap(request_json['id'], APLOG_USERBAN, APLOG_FAILURE, request_json if store_ap_json else None, 'Does not have permission') + return + + # request_json includes 'expires' and 'endTime' (same thing) but nowhere to record this and check in future for end in ban. + + if remove_data == True: + site_ban_remove_data(blocker.id, blocked) + log_incoming_ap(request_json['id'], APLOG_USERBAN, APLOG_SUCCESS, request_json if store_ap_json else None) + else: + #blocked.banned = True # uncommented until there's a mechanism for processing ban expiry date + #db.session.commit() + log_incoming_ap(request_json['id'], APLOG_USERBAN, APLOG_IGNORED, request_json if store_ap_json else None, 'Banned, but content retained') + return + # -- below this point is code that will be incrementally replaced to use log_incoming_ap() instead -- diff --git a/app/activitypub/util.py b/app/activitypub/util.py index a2669911..430f3e0f 100644 --- a/app/activitypub/util.py +++ b/app/activitypub/util.py @@ -1429,6 +1429,48 @@ def restore_post_or_comment(object_json, aplog_id): aplog.exception_message = 'Unable to resolve restorer or target' +def site_ban_remove_data(blocker_id, blocked): + replies = PostReply.query.filter_by(user_id=blocked.id, deleted=False) + for reply in replies: + reply.deleted = True + reply.deleted_by = blocker_id + if not blocked.bot: + reply.post.reply_count -= 1 + reply.community.post_reply_count -= 1 + blocked.reply_count = 0 + db.session.commit() + + posts = Post.query.filter_by(user_id=blocked.id, deleted=False) + for post in posts: + post.deleted = True + post.deleted_by = blocker_id + post.community.post_count -= 1 + if post.url and post.cross_posts is not None: + old_cross_posts = Post.query.filter(Post.id.in_(post.cross_posts)).all() + post.cross_posts.clear() + for ocp in old_cross_posts: + if ocp.cross_posts is not None and post.id in ocp.cross_posts: + ocp.cross_posts.remove(post.id) + blocked.post_count = 0 + db.session.commit() + + # Delete all their images to save moderators from having to see disgusting stuff. + # Images attached to posts can't be restored, but site ban reversals don't have a 'removeData' field anyway. + files = File.query.join(Post).filter(Post.user_id == blocked.id).all() + for file in files: + file.delete_from_disk() + file.source_url = '' + if blocked.avatar_id: + blocked.avatar.delete_from_disk() + blocked.avatar.source_url = '' + if blocked.cover_id: + blocked.cover.delete_from_disk() + blocked.cover.source_url = '' + # blocked.banned = True # uncommented until there's a mechanism for processing ban expiry date + + db.session.commit() + + def remove_data_from_banned_user(deletor_ap_id, user_ap_id, target): if current_app.debug: remove_data_from_banned_user_task(deletor_ap_id, user_ap_id, target)