temporary instance bans

This commit is contained in:
rimu 2024-11-30 09:50:14 +13:00
parent 95f7b226d2
commit dd93048e96
6 changed files with 96 additions and 10 deletions

View file

@ -892,7 +892,10 @@ def process_inbox_request(request_json, store_ap_json):
if not blocked:
log_incoming_ap(announce_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']
# target = request_json['target'] # target is supposed to determine the scope - whether it is an instance-wide ban or just one community. Lemmy doesn't use it right though
# community = find_actor_or_create(target, create_if_not_found=False, community_only=True)
remove_data = request_json['removeData'] if 'removeData' in request_json else False
# Lemmy currently only sends userbans for admins banning local users
@ -902,15 +905,31 @@ def process_inbox_request(request_json, store_ap_json):
log_incoming_ap(announce_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 blocked.banned: # We may have already banned them - we don't want remote temp bans to over-ride our permanent bans
return
if remove_data == True:
if blocked.is_local(): # Sanity check
current_app.logger.error('Attempt to ban local user: ' + str(request_json))
return
blocked.banned = True
db.session.commit()
if 'expires' in request_json:
blocked.banned_until = request_json['expires']
elif 'endTime' in request_json:
blocked.banned_until = request_json['endTime']
try:
db.session.commit()
except: # I don't know the format of expires or endTime so let's see how this goes
db.session.rollback()
current_app.logger.error('could not save banned_until value: ' + str(request_json))
if remove_data:
site_ban_remove_data(blocker.id, blocked)
log_incoming_ap(announce_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(announce_id, APLOG_USERBAN, APLOG_IGNORED, request_json if store_ap_json else None, 'Banned, but content retained')
return
if request_json['type'] == 'Undo':
@ -1438,8 +1457,8 @@ def user_followers(actor):
@bp.route('/comment/<int:comment_id>', methods=['GET', 'HEAD'])
def comment_ap(comment_id):
if is_activitypub_request():
reply = PostReply.query.get_or_404(comment_id)
if is_activitypub_request():
reply_data = comment_model_to_json(reply) if request.method == 'GET' else []
resp = jsonify(reply_data)
resp.content_type = 'application/activity+json'
@ -1447,7 +1466,6 @@ def comment_ap(comment_id):
resp.headers.set('Link', f'<https://{current_app.config["SERVER_NAME"]}/comment/{reply.id}>; rel="alternate"; type="text/html"')
return resp
else:
reply = PostReply.query.get_or_404(comment_id)
return continue_discussion(reply.post.id, comment_id)

View file

@ -1453,7 +1453,6 @@ def site_ban_remove_data(blocker_id, blocked):
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()

View file

@ -209,6 +209,11 @@ def register(app):
db.session.execute(text('DELETE FROM "post_reply_vote" WHERE created_at < :cutoff'), {'cutoff': utcnow() - timedelta(days=28 * 6)})
db.session.commit()
# Un-ban after ban expires
db.session.execute(text('UPDATE "user" SET banned = false WHERE banned is true AND banned_until < :cutoff AND banned_until is not null'),
{'cutoff': utcnow()})
db.session.commit()
# Check for dormant or dead instances
try:
# Check for dormant or dead instances

View file

@ -135,12 +135,20 @@ class InstanceRole(db.Model):
user = db.relationship('User', lazy='joined')
# Instances that this user has blocked
class InstanceBlock(db.Model):
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True)
instance_id = db.Column(db.Integer, db.ForeignKey('instance.id'), primary_key=True)
created_at = db.Column(db.DateTime, default=utcnow)
# Instances that have banned this user
class InstanceBan(db.Model):
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True)
instance_id = db.Column(db.Integer, db.ForeignKey('instance.id'), primary_key=True)
banned_until = db.Column(db.DateTime)
class Conversation(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), index=True)
@ -649,7 +657,8 @@ class User(UserMixin, db.Model):
password_hash = db.Column(db.String(128))
verified = db.Column(db.Boolean, default=False)
verification_token = db.Column(db.String(16), index=True)
banned = db.Column(db.Boolean, default=False)
banned = db.Column(db.Boolean, default=False, index=True)
banned_until = db.Column(db.DateTime) # null == permanent ban
deleted = db.Column(db.Boolean, default=False)
deleted_by = db.Column(db.Integer, index=True)
about = db.Column(db.Text) # markdown
@ -2010,6 +2019,7 @@ class CommunityBan(db.Model):
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True) # person who is banned, not the banner
community_id = db.Column(db.Integer, db.ForeignKey('community.id'), primary_key=True)
banned_by = db.Column(db.Integer, db.ForeignKey('user.id'))
banned_until = db.Column(db.DateTime)
reason = db.Column(db.String(256))
created_at = db.Column(db.DateTime, default=utcnow)
ban_until = db.Column(db.DateTime)

View file

@ -470,8 +470,13 @@ def poll_vote(post_id):
def continue_discussion(post_id, comment_id):
post = Post.query.get_or_404(post_id)
comment = PostReply.query.get_or_404(comment_id)
if post.community.banned or post.deleted or comment.deleted:
if current_user.is_anonymous or not (current_user.is_authenticated and (current_user.is_admin() or current_user.is_staff())):
abort(404)
else:
flash(_('This comment has been deleted and is only visible to staff and admins.'), 'warning')
mods = post.community.moderators()
is_moderator = current_user.is_authenticated and any(mod.user_id == current_user.id for mod in mods)
if post.community.private_mods:

View file

@ -0,0 +1,49 @@
"""banned until
Revision ID: 20a3bae71dd7
Revises: d88b49617de0
Create Date: 2024-11-30 08:52:27.584637
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '20a3bae71dd7'
down_revision = 'd88b49617de0'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('instance_ban',
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('instance_id', sa.Integer(), nullable=False),
sa.Column('banned_until', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['instance_id'], ['instance.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('user_id', 'instance_id')
)
with op.batch_alter_table('community_ban', schema=None) as batch_op:
batch_op.add_column(sa.Column('banned_until', sa.DateTime(), nullable=True))
with op.batch_alter_table('user', schema=None) as batch_op:
batch_op.add_column(sa.Column('banned_until', sa.DateTime(), nullable=True))
batch_op.create_index(batch_op.f('ix_user_banned'), ['banned'], unique=False)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('user', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_user_banned'))
batch_op.drop_column('banned_until')
with op.batch_alter_table('community_ban', schema=None) as batch_op:
batch_op.drop_column('banned_until')
op.drop_table('instance_ban')
# ### end Alembic commands ###