feat: Censure reasons (#17)

* allow providing reasons

* display censure reasons

* show reasons
pull/20/head
Divided by Zer0 2023-09-04 23:33:52 +02:00 committed by GitHub
parent 9f72580c6d
commit 1ff5bad102
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 77 additions and 6 deletions

View File

@ -34,9 +34,13 @@ class Models:
'approvals': fields.Integer(description="The amount of endorsements this instance has given out"), 'approvals': fields.Integer(description="The amount of endorsements this instance has given out"),
'endorsements': fields.Integer(description="The amount of endorsements this instance has received"), 'endorsements': fields.Integer(description="The amount of endorsements this instance has received"),
'guarantor': fields.String(description="The domain of the instance which guaranteed this instance.", example="fediseer.com"), 'guarantor': fields.String(description="The domain of the instance which guaranteed this instance.", example="fediseer.com"),
'censure_reasons': fields.List(fields.String(description="The reasons instances have given for censuring this instance")),
}) })
self.response_model_model_Whitelist_get = api.model('WhitelistedInstances', { self.response_model_model_Whitelist_get = api.model('WhitelistedInstances', {
'instances': fields.List(fields.Nested(self.response_model_instances)), 'instances': fields.List(fields.Nested(self.response_model_instances)),
'domains': fields.List(fields.String(description="The instance domains as a list.")), 'domains': fields.List(fields.String(description="The instance domains as a list.")),
'csv': fields.String(description="The instance domains as a csv."), 'csv': fields.String(description="The instance domains as a csv."),
}) })
self.input_censures_modify = api.model('ModifyCensure', {
'reason': fields.String(required=False, description="The reason for this censure. No profanity or hate speech allowed!", example="csam"),
})

View File

@ -1,5 +1,6 @@
from fediseer.apis.v1.base import * from fediseer.apis.v1.base import *
from fediseer.classes.instance import Censure,Endorsement from fediseer.classes.instance import Censure,Endorsement
from fediseer.utils import sanitize_string
class CensuresGiven(Resource): class CensuresGiven(Resource):
get_parser = reqparse.RequestParser() get_parser = reqparse.RequestParser()
@ -22,12 +23,17 @@ class CensuresGiven(Resource):
if not instances: if not instances:
raise e.NotFound(f"No Instances found matching any of the provided domains. Have you remembered to register them?") raise e.NotFound(f"No Instances found matching any of the provided domains. Have you remembered to register them?")
instance_details = [] instance_details = []
for instance in database.get_all_censured_instances_by_censuring_id([instance.id for instance in instances]): for c_instance in database.get_all_censured_instances_by_censuring_id([instance.id for instance in instances]):
instance_details.append(instance.get_details()) censures = database.get_all_censure_reasons_for_censured_id(c_instance.id, [instance.id for instance in instances])
c_instance_details = c_instance.get_details()
if len(censures) > 0:
c_instance_details["censure_reasons"] = [censure.reason for censure in censures]
instance_details.append(c_instance_details)
if self.args.csv: if self.args.csv:
return {"csv": ",".join([instance["domain"] for instance in instance_details])},200 return {"csv": ",".join([instance["domain"] for instance in instance_details])},200
if self.args.domains: if self.args.domains:
return {"domains": [instance["domain"] for instance in instance_details]},200 return {"domains": [instance["domain"] for instance in instance_details]},200
return {"instances": instance_details},200 return {"instances": instance_details},200
class Censures(Resource): class Censures(Resource):
@ -48,8 +54,12 @@ class Censures(Resource):
if not instance: if not instance:
raise e.NotFound(f"No Instance found matching provided domain. Have you remembered to register it?") raise e.NotFound(f"No Instance found matching provided domain. Have you remembered to register it?")
instance_details = [] instance_details = []
for instance in database.get_all_censuring_instances_by_censured_id(instance.id): for c_instance in database.get_all_censuring_instances_by_censured_id(instance.id):
instance_details.append(instance.get_details()) censures = database.get_all_censure_reasons_for_censured_id(instance.id, [c_instance.id])
c_instance_details = c_instance.get_details()
if len(censures) > 0:
c_instance_details["censure_reasons"] = [censure.reason for censure in censures]
instance_details.append(c_instance_details)
if self.args.csv: if self.args.csv:
return {"csv": ",".join([instance["domain"] for instance in instance_details])},200 return {"csv": ",".join([instance["domain"] for instance in instance_details])},200
if self.args.domains: if self.args.domains:
@ -59,9 +69,10 @@ class Censures(Resource):
put_parser = reqparse.RequestParser() put_parser = reqparse.RequestParser()
put_parser.add_argument("apikey", type=str, required=True, help="The sending instance's API key.", location='headers') put_parser.add_argument("apikey", type=str, required=True, help="The sending instance's API key.", location='headers')
put_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers") put_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
put_parser.add_argument("reason", default=None, type=str, required=False, location="json")
@api.expect(put_parser) @api.expect(put_parser,models.input_censures_modify, validate=True)
@api.marshal_with(models.response_model_simple_response, code=200, description='Endorse Instance') @api.marshal_with(models.response_model_simple_response, code=200, description='Endorse Instance')
@api.response(400, 'Bad Request', models.response_model_error) @api.response(400, 'Bad Request', models.response_model_error)
@api.response(401, 'Invalid API Key', models.response_model_error) @api.response(401, 'Invalid API Key', models.response_model_error)
@ -93,9 +104,14 @@ class Censures(Resource):
raise e.BadRequest("You can't censure an instance you've endorsed! Please withdraw the endorsement first.") raise e.BadRequest("You can't censure an instance you've endorsed! Please withdraw the endorsement first.")
if database.get_censure(target_instance.id,instance.id): if database.get_censure(target_instance.id,instance.id):
return {"message":'OK'}, 200 return {"message":'OK'}, 200
reason = self.args.reason
if reason is not None:
reason = sanitize_string(reason)
new_censure = Censure( new_censure = Censure(
censuring_id=instance.id, censuring_id=instance.id,
censured_id=target_instance.id, censured_id=target_instance.id,
reason=reason,
) )
db.session.add(new_censure) db.session.add(new_censure)
db.session.commit() db.session.commit()
@ -103,6 +119,45 @@ class Censures(Resource):
return {"message":'Changed'}, 200 return {"message":'Changed'}, 200
patch_parser = reqparse.RequestParser()
patch_parser.add_argument("apikey", type=str, required=True, help="The sending instance's API key.", location='headers')
patch_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
patch_parser.add_argument("reason", default=None, type=str, required=False, location="json")
@api.expect(patch_parser,models.input_censures_modify, validate=True)
@api.marshal_with(models.response_model_simple_response, code=200, description='Endorse Instance')
@api.response(400, 'Bad Request', models.response_model_error)
@api.response(401, 'Invalid API Key', models.response_model_error)
@api.response(403, 'Not Guaranteed', models.response_model_error)
@api.response(404, 'Instance not registered', models.response_model_error)
def patch(self, domain):
'''Modify an instance's Censure
'''
self.args = self.patch_parser.parse_args()
if not self.args.apikey:
raise e.Unauthorized("You must provide the API key that was PM'd to your admin account")
instance = database.find_instance_by_api_key(self.args.apikey)
if not instance:
raise e.NotFound(f"No Instance found matching provided API key and domain. Have you remembered to register it?")
target_instance = database.find_instance_by_domain(domain=domain)
if not target_instance:
raise e.BadRequest("Instance from which to modify censure not found")
censure = database.get_censure(target_instance.id,instance.id)
if not censure:
raise e.BadRequest(f"No censure found for {domain} from {instance.domain}")
reason = self.args.reason
if reason is not None:
reason = sanitize_string(reason)
logger.debug([censure.reason,reason])
if censure.reason == reason:
return {"message":'OK'}, 200
censure.reason = reason
db.session.commit()
logger.info(f"{instance.domain} Modfied censure for {domain}")
return {"message":'Changed'}, 200
delete_parser = reqparse.RequestParser() delete_parser = reqparse.RequestParser()
delete_parser.add_argument("apikey", type=str, required=True, help="The sending instance's API key.", location='headers') delete_parser.add_argument("apikey", type=str, required=True, help="The sending instance's API key.", location='headers')
delete_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers") delete_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")

View File

@ -52,6 +52,7 @@ class Censure(db.Model):
__tablename__ = "censures" __tablename__ = "censures"
__table_args__ = (UniqueConstraint('censuring_id', 'censured_id', name='censures_censuring_id_censured_id'),) __table_args__ = (UniqueConstraint('censuring_id', 'censured_id', name='censures_censuring_id_censured_id'),)
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
reason = db.Column(db.String(255), unique=False, nullable=True, index=False)
censuring_id = db.Column(db.Integer, db.ForeignKey("instances.id", ondelete="CASCADE"), nullable=False) censuring_id = db.Column(db.Integer, db.ForeignKey("instances.id", ondelete="CASCADE"), nullable=False)
censuring_instance = db.relationship("Instance", back_populates="censures_given", foreign_keys=[censuring_id]) censuring_instance = db.relationship("Instance", back_populates="censures_given", foreign_keys=[censuring_id])
censured_id = db.Column(db.Integer, db.ForeignKey("instances.id", ondelete="CASCADE"), nullable=False) censured_id = db.Column(db.Integer, db.ForeignKey("instances.id", ondelete="CASCADE"), nullable=False)

View File

@ -91,6 +91,16 @@ def get_all_censuring_instances_by_censured_id(censured_id):
) )
return query.all() return query.all()
def get_all_censure_reasons_for_censured_id(censured_id, censuring_ids):
query = Censure.query.filter(
and_(
Censure.censured_id == censured_id,
Censure.censuring_id.in_(censuring_ids),
Censure.reason != None
)
).with_entities(Censure.reason)
return query.all()
def get_all_guaranteed_instances_by_guarantor_id(guarantor_id): def get_all_guaranteed_instances_by_guarantor_id(guarantor_id):

View File

@ -17,3 +17,4 @@ pythorhead>=0.8.2
bleach bleach
boto3 boto3
pybadges pybadges