feat: list visibility

pull/26/head
db0 2023-09-18 01:06:58 +02:00
parent bb4edcecda
commit c616ad3cb5
10 changed files with 232 additions and 26 deletions

View File

@ -1,5 +1,14 @@
# Changelog # Changelog
# 0.16.0
Allows instances to control the visibility of their endorsements, censures and hesitations by setting their visibility in PATCHing `api/b1/whitelist`
* OPEN: Public visibility (Default).
* ENDORSED: Only endorsed instances can see that list.
* PRIVATE: Only admins from their own instance can see that list.
When a list visibility is not OPEN, the reports will use `[REDACTED]` as the target domain. But this will not affect historical reports.
# 0.15.1 # 0.15.1
* Added some rate limits. Currently each instance is limited to 20 actions per minute * Added some rate limits. Currently each instance is limited to 20 actions per minute

View File

@ -88,12 +88,15 @@ class Models:
'guarantor': fields.String(required=False, description="(Optional) The domain of the guaranteeing instance. They will receive a PM to validate you", example="lemmy.dbzer0.com"), 'guarantor': fields.String(required=False, description="(Optional) The domain of the guaranteeing instance. They will receive a PM to validate you", example="lemmy.dbzer0.com"),
'pm_proxy': fields.String(required=False, enum=[e.name for e in enums.PMProxy], description="(Optional) If you do receive the PM from @fediseer@fediseer.com, set this to 'MASTODON' to make the Fediseer PM your your API key via @fediseer@botsin.space. For this to work, ensure that botsin.space is not blocked in your instance and optimally follow @fediseer@botsin.space as well. If set, this will be used permanently for communication to your instance."), 'pm_proxy': fields.String(required=False, enum=[e.name for e in enums.PMProxy], description="(Optional) If you do receive the PM from @fediseer@fediseer.com, set this to 'MASTODON' to make the Fediseer PM your your API key via @fediseer@botsin.space. For this to work, ensure that botsin.space is not blocked in your instance and optimally follow @fediseer@botsin.space as well. If set, this will be used permanently for communication to your instance."),
}) })
self.input_api_key_reset = api.model('ApiKeyResetInput', { self.input_instance_modify = api.model('InstanceModify', {
'admin_username': fields.String(required=False, description="If a username is given, their API key will be reset. Otherwise the user's whose API key was provided will be reset. This allows can be initiated by other instance admins or the fediseer.", example="admin"), 'admin_username': fields.String(required=False, description="If a username is given, their API key will be reset. Otherwise the user's whose API key was provided will be reset. This allows can be initiated by other instance admins or the fediseer.", example="admin"),
'return_new_key': fields.Boolean(required=False, default=False, description="If True, the key will be returned as part of the response instead of PM'd. Fediseer will still PM a notification to the target admin account."), 'return_new_key': fields.Boolean(required=False, default=False, description="If True, the key will be returned as part of the response instead of PM'd. Fediseer will still PM a notification to the target admin account."),
'sysadmins': fields.Integer(required=False, default=None, min=0, max=100, description="Report how many system administrators this instance currently has."), 'sysadmins': fields.Integer(required=False, default=None, min=0, max=100, description="Report how many system administrators this instance currently has."),
'moderators': fields.Integer(required=False, default=None, min=0, max=1000, description="Report how many instance moderators this instance currently has."), 'moderators': fields.Integer(required=False, default=None, min=0, max=1000, description="Report how many instance moderators this instance currently has."),
'pm_proxy': fields.String(required=False, enum=[e.name for e in enums.PMProxy], description="(Optional) If you do receive the PM from @fediseer@fediseer.com, set this to 'MASTODON' to make the Fediseer PM your your API key via @fediseer@botsin.space. For this to work, ensure that botsin.space is not blocked in your instance and optimally follow @fediseer@botsin.space as well. If set, this will be used permanently for communication to your instance."), 'pm_proxy': fields.String(required=False, enum=[e.name for e in enums.PMProxy], description="(Optional) If you do receive the PM from @fediseer@fediseer.com, set this to 'MASTODON' to make the Fediseer PM your your API key via @fediseer@botsin.space. For this to work, ensure that botsin.space is not blocked in your instance and optimally follow @fediseer@botsin.space as well. If set, this will be used permanently for communication to your instance."),
'visibility_endorsements': fields.String(required=False, enum=[e.name for e in enums.ListVisibility], description="Set this to OPEN, to allow anyone to read your endorsements. Set to ENDORSED to only allow endorsed instances to read your endorsements. Set to PRIVATE to only allow your own admins to read your endorsements."),
'visibility_censures': fields.String(required=False, enum=[e.name for e in enums.ListVisibility], description="Set this to OPEN, to allow anyone to read your censures. Set to ENDORSED to only allow endorsed instances to read your censures. Set to PRIVATE to only allow your own admins to read your censures."),
'visibility_hesitations': fields.String(required=False, enum=[e.name for e in enums.ListVisibility], description="Set this to OPEN, to allow anyone to read your hesitations. Set to ENDORSED to only allow endorsed instances to read your hesitations. Set to PRIVATE to only allow your own admins to read your hesitations."),
}) })
self.response_model_reports = api.model('ActivityReport', { self.response_model_reports = api.model('ActivityReport', {
'source_domain': fields.String(description="The instance domain which initiated this activity", example="lemmy.dbzer0.com"), 'source_domain': fields.String(description="The instance domain which initiated this activity", example="lemmy.dbzer0.com"),

View File

@ -6,6 +6,7 @@ from fediseer import enums
class CensuresGiven(Resource): class CensuresGiven(Resource):
get_parser = reqparse.RequestParser() get_parser = reqparse.RequestParser()
get_parser.add_argument("apikey", type=str, required=False, help="An instance's API key.", location='headers')
get_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers") get_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
get_parser.add_argument("csv", required=False, type=bool, help="Set to true to return just the domains as a csv. Mutually exclusive with domains", location="args") get_parser.add_argument("csv", required=False, type=bool, help="Set to true to return just the domains as a csv. Mutually exclusive with domains", location="args")
get_parser.add_argument("domains", required=False, type=bool, help="Set to true to return just the domains as a list. Mutually exclusive with csv", location="args") get_parser.add_argument("domains", required=False, type=bool, help="Set to true to return just the domains as a list. Mutually exclusive with csv", location="args")
@ -16,17 +17,39 @@ class CensuresGiven(Resource):
@cache.cached(timeout=10, query_string=True) @cache.cached(timeout=10, query_string=True)
@api.marshal_with(models.response_model_model_Censures_get, code=200, description='Instances', skip_none=True) @api.marshal_with(models.response_model_model_Censures_get, code=200, description='Instances', skip_none=True)
@api.response(404, 'Instance not registered', models.response_model_error) @api.response(404, 'Instance not registered', models.response_model_error)
@api.response(401, 'API key not found', models.response_model_error)
@api.response(403, 'Access Denied', models.response_model_error)
def get(self, domains_csv): def get(self, domains_csv):
'''Display all censures given out by one or more domains '''Display all censures given out by one or more domains
You can pass a comma-separated list of domain names You can pass a comma-separated list of domain names
and the results will be a set of all their censures together. and the results will be a set of all their censures together.
''' '''
self.args = self.get_parser.parse_args() self.args = self.get_parser.parse_args()
get_instance = None
if self.args.apikey:
get_instance = database.find_instance_by_api_key(self.args.apikey)
if not get_instance:
raise e.Unauthorized(f"No Instance found matching provided API key. Please ensure you've typed it correctly")
domains_list = domains_csv.split(',') domains_list = domains_csv.split(',')
instances = database.find_multiple_instance_by_domains(domains_list) precheck_instances = database.find_multiple_instance_by_domains(domains_list)
if not instances: if not precheck_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?")
if self.args.min_censures > len(domains_list): instances = []
for instance in precheck_instances:
if instance.visibility_censures == enums.ListVisibility.ENDORSED:
if get_instance is None:
continue
if instance != get_instance and not instance.is_endorsing(get_instance):
continue
if instance.visibility_censures == enums.ListVisibility.PRIVATE:
if get_instance is None:
continue
if instance != get_instance:
continue
instances.append(instance)
if len(instances) == 0:
raise e.Forbidden(f"You do not have access to see these censures")
if self.args.min_censures > len(instances):
raise e.BadRequest(f"You cannot request more censures than the amount of reference domains") raise e.BadRequest(f"You cannot request more censures than the amount of reference domains")
instance_details = [] instance_details = []
for c_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]):
@ -78,6 +101,7 @@ class CensuresGiven(Resource):
class Censures(Resource): class Censures(Resource):
get_parser = reqparse.RequestParser() get_parser = reqparse.RequestParser()
get_parser.add_argument("apikey", type=str, required=False, help="An instance's API key.", location='headers')
get_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers") get_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
get_parser.add_argument("csv", required=False, type=bool, help="Set to true to return just the domains as a csv. Mutually exclusive with domains", location="args") get_parser.add_argument("csv", required=False, type=bool, help="Set to true to return just the domains as a csv. Mutually exclusive with domains", location="args")
get_parser.add_argument("domains", required=False, type=bool, help="Set to true to return just the domains as a list. Mutually exclusive with csv", location="args") get_parser.add_argument("domains", required=False, type=bool, help="Set to true to return just the domains as a list. Mutually exclusive with csv", location="args")
@ -90,11 +114,30 @@ class Censures(Resource):
'''Display all censures received by a specific domain '''Display all censures received by a specific domain
''' '''
self.args = self.get_parser.parse_args() self.args = self.get_parser.parse_args()
get_instance = None
if self.args.apikey:
get_instance = database.find_instance_by_api_key(self.args.apikey)
if not get_instance:
raise e.Unauthorized(f"No Instance found matching provided API key. Please ensure you've typed it correctly")
instance = database.find_instance_by_domain(domain) instance = database.find_instance_by_domain(domain)
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 c_instance in database.get_all_censuring_instances_by_censured_id(instance.id): precheck_instances = database.get_all_censuring_instances_by_censured_id(instance.id)
instances = []
for instance in precheck_instances:
if instance.visibility_censures == enums.ListVisibility.ENDORSED:
if get_instance is None:
continue
if not instance.is_endorsing(get_instance):
continue
if instance.visibility_censures == enums.ListVisibility.PRIVATE:
if get_instance is None:
continue
if not instance != get_instance:
continue
instances.append(instance)
for c_instance in instances:
censures = database.get_all_censure_reasons_for_censured_id(instance.id, [c_instance.id]) censures = database.get_all_censure_reasons_for_censured_id(instance.id, [c_instance.id])
censures = [c for c in censures if c.reason is not None] censures = [c for c in censures if c.reason is not None]
c_instance_details = c_instance.get_details() c_instance_details = c_instance.get_details()
@ -164,9 +207,12 @@ class Censures(Resource):
evidence=evidence, evidence=evidence,
) )
db.session.add(new_censure) db.session.add(new_censure)
target_domain = target_instance.domain
if instance.visibility_censures != enums.ListVisibility.OPEN:
target_domain = '[REDACTED]'
new_report = Report( new_report = Report(
source_domain=instance.domain, source_domain=instance.domain,
target_domain=target_instance.domain, target_domain=target_domain,
report_type=enums.ReportType.CENSURE, report_type=enums.ReportType.CENSURE,
report_activity=enums.ReportActivity.ADDED, report_activity=enums.ReportActivity.ADDED,
) )
@ -222,9 +268,12 @@ class Censures(Resource):
changed = True changed = True
if changed is False: if changed is False:
return {"message":'OK'}, 200 return {"message":'OK'}, 200
target_domain = target_instance.domain
if instance.visibility_censures != enums.ListVisibility.OPEN:
target_domain = '[REDACTED]'
new_report = Report( new_report = Report(
source_domain=instance.domain, source_domain=instance.domain,
target_domain=target_instance.domain, target_domain=target_domain,
report_type=enums.ReportType.CENSURE, report_type=enums.ReportType.CENSURE,
report_activity=enums.ReportActivity.MODIFIED, report_activity=enums.ReportActivity.MODIFIED,
) )
@ -260,9 +309,12 @@ class Censures(Resource):
if not censure: if not censure:
return {"message":'OK'}, 200 return {"message":'OK'}, 200
db.session.delete(censure) db.session.delete(censure)
target_domain = target_instance.domain
if instance.visibility_censures != enums.ListVisibility.OPEN:
target_domain = '[REDACTED]'
new_report = Report( new_report = Report(
source_domain=instance.domain, source_domain=instance.domain,
target_domain=target_instance.domain, target_domain=target_domain,
report_type=enums.ReportType.CENSURE, report_type=enums.ReportType.CENSURE,
report_activity=enums.ReportActivity.DELETED, report_activity=enums.ReportActivity.DELETED,
) )

View File

@ -6,6 +6,7 @@ from fediseer.utils import sanitize_string
class Approvals(Resource): class Approvals(Resource):
get_parser = reqparse.RequestParser() get_parser = reqparse.RequestParser()
get_parser.add_argument("apikey", type=str, required=False, help="An instance's API key.", location='headers')
get_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers") get_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
get_parser.add_argument("csv", required=False, type=bool, help="Set to true to return just the domains as a csv. Mutually exclusive with domains", location="args") get_parser.add_argument("csv", required=False, type=bool, help="Set to true to return just the domains as a csv. Mutually exclusive with domains", location="args")
get_parser.add_argument("domains", required=False, type=bool, help="Set to true to return just the domains as a list. Mutually exclusive with csv", location="args") get_parser.add_argument("domains", required=False, type=bool, help="Set to true to return just the domains as a list. Mutually exclusive with csv", location="args")
@ -16,6 +17,8 @@ class Approvals(Resource):
@cache.cached(timeout=10, query_string=True) @cache.cached(timeout=10, query_string=True)
@api.marshal_with(models.response_model_model_Endorsed_get, code=200, description='Instances', skip_none=True) @api.marshal_with(models.response_model_model_Endorsed_get, code=200, description='Instances', skip_none=True)
@api.response(404, 'Instance not registered', models.response_model_error) @api.response(404, 'Instance not registered', models.response_model_error)
@api.response(401, 'API key not found', models.response_model_error)
@api.response(403, 'Access Denied', models.response_model_error)
def get(self, domains_csv): def get(self, domains_csv):
'''Display all endorsements given out by one or more domains '''Display all endorsements given out by one or more domains
You can pass a comma-separated list of domain names and the results will be a set of all their You can pass a comma-separated list of domain names and the results will be a set of all their
@ -23,9 +26,29 @@ class Approvals(Resource):
''' '''
domains_list = domains_csv.split(',') domains_list = domains_csv.split(',')
self.args = self.get_parser.parse_args() self.args = self.get_parser.parse_args()
instances = database.find_multiple_instance_by_domains(domains_list) get_instance = None
if not instances: if self.args.apikey:
get_instance = database.find_instance_by_api_key(self.args.apikey)
if not get_instance:
raise e.Unauthorized(f"No Instance found matching provided API key. Please ensure you've typed it correctly")
precheck_instances = database.find_multiple_instance_by_domains(domains_list)
if not precheck_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?")
instances = []
for instance in precheck_instances:
if instance.visibility_endorsements == enums.ListVisibility.ENDORSED:
if get_instance is None:
continue
if instance != get_instance and not instance.is_endorsing(get_instance):
continue
if instance.visibility_endorsements == enums.ListVisibility.PRIVATE:
if get_instance is None:
continue
if instance != get_instance:
continue
instances.append(instance)
if len(instances) == 0:
raise e.Forbidden(f"You do not have access to see these endorsements")
instance_details = [] instance_details = []
for e_instance in database.get_all_endorsed_instances_by_approving_id([instance.id for instance in instances]): for e_instance in database.get_all_endorsed_instances_by_approving_id([instance.id for instance in instances]):
endorsements = database.get_all_endorsement_reasons_for_endorsed_id(e_instance.id, [instance.id for instance in instances]) endorsements = database.get_all_endorsement_reasons_for_endorsed_id(e_instance.id, [instance.id for instance in instances])
@ -58,6 +81,7 @@ class Approvals(Resource):
class Endorsements(Resource): class Endorsements(Resource):
get_parser = reqparse.RequestParser() get_parser = reqparse.RequestParser()
get_parser.add_argument("apikey", type=str, required=False, help="An instance's API key.", location='headers')
get_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers") get_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
get_parser.add_argument("csv", required=False, type=bool, help="Set to true to return just the domains as a csv. Mutually exclusive with domains", location="args") get_parser.add_argument("csv", required=False, type=bool, help="Set to true to return just the domains as a csv. Mutually exclusive with domains", location="args")
get_parser.add_argument("domains", required=False, type=bool, help="Set to true to return just the domains as a list. Mutually exclusive with csv", location="args") get_parser.add_argument("domains", required=False, type=bool, help="Set to true to return just the domains as a list. Mutually exclusive with csv", location="args")
@ -70,11 +94,30 @@ class Endorsements(Resource):
'''Display all endorsements received by a specific domain '''Display all endorsements received by a specific domain
''' '''
self.args = self.get_parser.parse_args() self.args = self.get_parser.parse_args()
get_instance = None
if self.args.apikey:
get_instance = database.find_instance_by_api_key(self.args.apikey)
if not get_instance:
raise e.Unauthorized(f"No Instance found matching provided API key. Please ensure you've typed it correctly")
instance = database.find_instance_by_domain(domain) instance = database.find_instance_by_domain(domain)
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 e_instance in database.get_all_approving_instances_by_endorsed_id(instance.id): precheck_instances = database.get_all_approving_instances_by_endorsed_id(instance.id)
instances = []
for instance in precheck_instances:
if instance.visibility_endorsements == enums.ListVisibility.ENDORSED:
if get_instance is None:
continue
if not instance.is_endorsing(get_instance):
continue
if instance.visibility_endorsements == enums.ListVisibility.PRIVATE:
if get_instance is None:
continue
if not instance != get_instance:
continue
instances.append(instance)
for e_instance in instances:
endorsements = database.get_all_endorsement_reasons_for_endorsed_id(instance.id, [e_instance.id]) endorsements = database.get_all_endorsement_reasons_for_endorsed_id(instance.id, [e_instance.id])
endorsements = [e for e in endorsements if e.reason is not None] endorsements = [e for e in endorsements if e.reason is not None]
e_instance_details = e_instance.get_details() e_instance_details = e_instance.get_details()
@ -139,9 +182,12 @@ class Endorsements(Resource):
reason=reason, reason=reason,
) )
db.session.add(new_endorsement) db.session.add(new_endorsement)
target_domain = target_instance.domain
if instance.visibility_endorsements != enums.ListVisibility.OPEN:
target_domain = '[REDACTED]'
new_report = Report( new_report = Report(
source_domain=instance.domain, source_domain=instance.domain,
target_domain=target_instance.domain, target_domain=target_domain,
report_type=enums.ReportType.ENDORSEMENT, report_type=enums.ReportType.ENDORSEMENT,
report_activity=enums.ReportActivity.ADDED, report_activity=enums.ReportActivity.ADDED,
) )
@ -202,9 +248,12 @@ class Endorsements(Resource):
changed = True changed = True
if changed is False: if changed is False:
return {"message":'OK'}, 200 return {"message":'OK'}, 200
target_domain = target_instance.domain
if instance.visibility_endorsements != enums.ListVisibility.OPEN:
target_domain = '[REDACTED]'
new_report = Report( new_report = Report(
source_domain=instance.domain, source_domain=instance.domain,
target_domain=target_instance.domain, target_domain=target_domain,
report_type=enums.ReportType.ENDORSEMENT, report_type=enums.ReportType.ENDORSEMENT,
report_activity=enums.ReportActivity.MODIFIED, report_activity=enums.ReportActivity.MODIFIED,
) )
@ -242,9 +291,12 @@ class Endorsements(Resource):
if not endorsement: if not endorsement:
return {"message":'OK'}, 200 return {"message":'OK'}, 200
db.session.delete(endorsement) db.session.delete(endorsement)
target_domain = target_instance.domain
if instance.visibility_endorsements != enums.ListVisibility.OPEN:
target_domain = '[REDACTED]'
new_report = Report( new_report = Report(
source_domain=instance.domain, source_domain=instance.domain,
target_domain=target_instance.domain, target_domain=target_domain,
report_type=enums.ReportType.ENDORSEMENT, report_type=enums.ReportType.ENDORSEMENT,
report_activity=enums.ReportActivity.DELETED, report_activity=enums.ReportActivity.DELETED,
) )

View File

@ -6,6 +6,7 @@ from fediseer import enums
class HesitationsGiven(Resource): class HesitationsGiven(Resource):
get_parser = reqparse.RequestParser() get_parser = reqparse.RequestParser()
get_parser.add_argument("apikey", type=str, required=False, help="An instance's API key.", location='headers')
get_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers") get_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
get_parser.add_argument("csv", required=False, type=bool, help="Set to true to return just the domains as a csv. Mutually exclusive with domains", location="args") get_parser.add_argument("csv", required=False, type=bool, help="Set to true to return just the domains as a csv. Mutually exclusive with domains", location="args")
get_parser.add_argument("domains", required=False, type=bool, help="Set to true to return just the domains as a list. Mutually exclusive with csv", location="args") get_parser.add_argument("domains", required=False, type=bool, help="Set to true to return just the domains as a list. Mutually exclusive with csv", location="args")
@ -16,17 +17,40 @@ class HesitationsGiven(Resource):
@cache.cached(timeout=10, query_string=True) @cache.cached(timeout=10, query_string=True)
@api.marshal_with(models.response_model_model_Hesitations_get, code=200, description='Instances', skip_none=True) @api.marshal_with(models.response_model_model_Hesitations_get, code=200, description='Instances', skip_none=True)
@api.response(404, 'Instance not registered', models.response_model_error) @api.response(404, 'Instance not registered', models.response_model_error)
@api.response(401, 'API key not found', models.response_model_error)
@api.response(403, 'Access Denied', models.response_model_error)
def get(self, domains_csv): def get(self, domains_csv):
'''Display all hesitations given out by one or more domains '''Display all hesitations given out by one or more domains
You can pass a comma-separated list of domain names You can pass a comma-separated list of domain names
and the results will be a set of all their hesitations together. and the results will be a set of all their hesitations together.
''' '''
self.args = self.get_parser.parse_args() self.args = self.get_parser.parse_args()
get_instance = None
if self.args.apikey:
get_instance = database.find_instance_by_api_key(self.args.apikey)
if not get_instance:
raise e.Unauthorized(f"No Instance found matching provided API key. Please ensure you've typed it correctly")
domains_list = domains_csv.split(',') domains_list = domains_csv.split(',')
instances = database.find_multiple_instance_by_domains(domains_list) precheck_instances = database.find_multiple_instance_by_domains(domains_list)
if not instances: if not precheck_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?")
if self.args.min_hesitations > len(domains_list): instances = []
for instance in precheck_instances:
if instance.visibility_hesitations == enums.ListVisibility.ENDORSED:
if get_instance is None:
continue
if instance != get_instance and not instance.is_endorsing(get_instance):
continue
if instance.visibility_hesitations == enums.ListVisibility.PRIVATE:
logger.debug([instance.visibility_hesitations,instance,get_instance,instance != get_instance])
if get_instance is None:
continue
if instance != get_instance:
continue
instances.append(instance)
if len(instances) == 0:
raise e.Forbidden(f"You do not have access to see these hesitations")
if self.args.min_hesitations > len(instances):
raise e.BadRequest(f"You cannot request more hesitations than the amount of reference domains") raise e.BadRequest(f"You cannot request more hesitations than the amount of reference domains")
instance_details = [] instance_details = []
for c_instance in database.get_all_dubious_instances_by_hesitant_id([instance.id for instance in instances]): for c_instance in database.get_all_dubious_instances_by_hesitant_id([instance.id for instance in instances]):
@ -63,6 +87,7 @@ class HesitationsGiven(Resource):
class Hesitations(Resource): class Hesitations(Resource):
get_parser = reqparse.RequestParser() get_parser = reqparse.RequestParser()
get_parser.add_argument("apikey", type=str, required=False, help="An instance's API key.", location='headers')
get_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers") get_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
get_parser.add_argument("csv", required=False, type=bool, help="Set to true to return just the domains as a csv. Mutually exclusive with domains", location="args") get_parser.add_argument("csv", required=False, type=bool, help="Set to true to return just the domains as a csv. Mutually exclusive with domains", location="args")
get_parser.add_argument("domains", required=False, type=bool, help="Set to true to return just the domains as a list. Mutually exclusive with csv", location="args") get_parser.add_argument("domains", required=False, type=bool, help="Set to true to return just the domains as a list. Mutually exclusive with csv", location="args")
@ -75,11 +100,30 @@ class Hesitations(Resource):
'''Display all hesitations received by a specific domain '''Display all hesitations received by a specific domain
''' '''
self.args = self.get_parser.parse_args() self.args = self.get_parser.parse_args()
get_instance = None
if self.args.apikey:
get_instance = database.find_instance_by_api_key(self.args.apikey)
if not get_instance:
raise e.Unauthorized(f"No Instance found matching provided API key. Please ensure you've typed it correctly")
instance = database.find_instance_by_domain(domain) instance = database.find_instance_by_domain(domain)
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?")
precheck_instances = database.get_all_hesitant_instances_by_dubious_id(instance.id)
instances = []
for instance in precheck_instances:
if instance.visibility_endorsements == enums.ListVisibility.ENDORSED:
if get_instance is None:
continue
if not instance.is_endorsing(get_instance):
continue
if instance.visibility_endorsements == enums.ListVisibility.PRIVATE:
if get_instance is None:
continue
if not instance != get_instance:
continue
instances.append(instance)
instance_details = [] instance_details = []
for c_instance in database.get_all_hesitant_instances_by_dubious_id(instance.id): for c_instance in instances:
hesitations = database.get_all_hesitation_reasons_for_dubious_id(instance.id, [c_instance.id]) hesitations = database.get_all_hesitation_reasons_for_dubious_id(instance.id, [c_instance.id])
hesitations = [c for c in hesitations if c.reason is not None] hesitations = [c for c in hesitations if c.reason is not None]
c_instance_details = c_instance.get_details() c_instance_details = c_instance.get_details()
@ -149,9 +193,12 @@ class Hesitations(Resource):
evidence=evidence, evidence=evidence,
) )
db.session.add(new_hesitation) db.session.add(new_hesitation)
target_domain = target_instance.domain
if instance.visibility_hesitations != enums.ListVisibility.OPEN:
target_domain = '[REDACTED]'
new_report = Report( new_report = Report(
source_domain=instance.domain, source_domain=instance.domain,
target_domain=target_instance.domain, target_domain=target_domain,
report_type=enums.ReportType.HESITATION, report_type=enums.ReportType.HESITATION,
report_activity=enums.ReportActivity.ADDED, report_activity=enums.ReportActivity.ADDED,
) )
@ -206,9 +253,12 @@ class Hesitations(Resource):
changed = True changed = True
if changed is False: if changed is False:
return {"message":'OK'}, 200 return {"message":'OK'}, 200
target_domain = target_instance.domain
if instance.visibility_hesitations != enums.ListVisibility.OPEN:
target_domain = '[REDACTED]'
new_report = Report( new_report = Report(
source_domain=instance.domain, source_domain=instance.domain,
target_domain=target_instance.domain, target_domain=target_domain,
report_type=enums.ReportType.HESITATION, report_type=enums.ReportType.HESITATION,
report_activity=enums.ReportActivity.MODIFIED, report_activity=enums.ReportActivity.MODIFIED,
) )
@ -245,9 +295,12 @@ class Hesitations(Resource):
if not hesitation: if not hesitation:
return {"message":'OK'}, 200 return {"message":'OK'}, 200
db.session.delete(hesitation) db.session.delete(hesitation)
target_domain = target_instance.domain
if instance.visibility_hesitations != enums.ListVisibility.OPEN:
target_domain = '[REDACTED]'
new_report = Report( new_report = Report(
source_domain=instance.domain, source_domain=instance.domain,
target_domain=target_instance.domain, target_domain=target_domain,
report_type=enums.ReportType.HESITATION, report_type=enums.ReportType.HESITATION,
report_activity=enums.ReportActivity.DELETED, report_activity=enums.ReportActivity.DELETED,
) )

View File

@ -145,9 +145,12 @@ class WhitelistDomain(Resource):
patch_parser.add_argument("sysadmins", default=None, required=False, type=int, help="How many sysadmins this instance has.", location="json") patch_parser.add_argument("sysadmins", default=None, required=False, type=int, help="How many sysadmins this instance has.", location="json")
patch_parser.add_argument("moderators", default=None, required=False, type=int, help="How many moderators this instance has.", location="json") patch_parser.add_argument("moderators", default=None, required=False, type=int, help="How many moderators this instance has.", location="json")
patch_parser.add_argument("pm_proxy", required=False, type=str, help="(Optional) If you do receive the PM from @fediseer@fediseer.com, set this to true to make the Fediseer PM your your API key via @fediseer@botsin.space. For this to work, ensure that botsin.space is not blocked in your instance and optimally follow @fediseer@botsin.space as well. If set, this will be used permanently for communication to your instance.", location="json") patch_parser.add_argument("pm_proxy", required=False, type=str, help="(Optional) If you do receive the PM from @fediseer@fediseer.com, set this to true to make the Fediseer PM your your API key via @fediseer@botsin.space. For this to work, ensure that botsin.space is not blocked in your instance and optimally follow @fediseer@botsin.space as well. If set, this will be used permanently for communication to your instance.", location="json")
patch_parser.add_argument("visibility_endorsements", required=False, type=str, location="json")
patch_parser.add_argument("visibility_censures", required=False, type=str, location="json")
patch_parser.add_argument("visibility_hesitations", required=False, type=str, location="json")
@api.expect(patch_parser,models.input_api_key_reset, validate=True) @api.expect(patch_parser,models.input_instance_modify, validate=True)
@api.marshal_with(models.response_model_api_key_reset, code=200, description='Instances', skip_none=True) @api.marshal_with(models.response_model_api_key_reset, code=200, description='Instances', skip_none=True)
@api.response(401, 'Invalid API Key', models.response_model_error) @api.response(401, 'Invalid API Key', models.response_model_error)
@api.response(403, 'Instance Not Registered', models.response_model_error) @api.response(403, 'Instance Not Registered', models.response_model_error)
@ -171,12 +174,26 @@ class WhitelistDomain(Resource):
instance.moderators = self.args.moderators instance.moderators = self.args.moderators
changed = True changed = True
if self.args.pm_proxy is not None: if self.args.pm_proxy is not None:
logger.debug(self.args.pm_proxy)
proxy = enums.PMProxy[self.args.pm_proxy] proxy = enums.PMProxy[self.args.pm_proxy]
if instance.pm_proxy != proxy: if instance.pm_proxy != proxy:
activitypub_pm.pm_new_proxy_switch(proxy,instance.pm_proxy,instance,user.username) activitypub_pm.pm_new_proxy_switch(proxy,instance.pm_proxy,instance,user.username)
instance.pm_proxy = proxy instance.pm_proxy = proxy
changed = True changed = True
if self.args.visibility_endorsements is not None:
visibility = enums.ListVisibility[self.args.visibility_endorsements]
if instance.visibility_endorsements != visibility:
instance.visibility_endorsements = visibility
changed = True
if self.args.visibility_censures is not None:
visibility = enums.ListVisibility[self.args.visibility_censures]
if instance.visibility_censures != visibility:
instance.visibility_censures = visibility
changed = True
if self.args.visibility_hesitations is not None:
visibility = enums.ListVisibility[self.args.visibility_hesitations]
if instance.visibility_hesitations != visibility:
instance.visibility_hesitations = visibility
changed = True
if self.args.admin_username: if self.args.admin_username:
requestor = None requestor = None
if self.args.admin_username != user.username or user.username == "fediseer": if self.args.admin_username != user.username or user.username == "fediseer":

View File

@ -100,7 +100,9 @@ class Instance(db.Model):
sysadmins = db.Column(db.Integer, unique=False, nullable=True) sysadmins = db.Column(db.Integer, unique=False, nullable=True)
moderators = db.Column(db.Integer, unique=False, nullable=True) moderators = db.Column(db.Integer, unique=False, nullable=True)
pm_proxy = db.Column(Enum(enums.PMProxy), default=enums.PMProxy.NONE, nullable=False) pm_proxy = db.Column(Enum(enums.PMProxy), default=enums.PMProxy.NONE, nullable=False)
visibility_endorsements = db.Column(Enum(enums.ListVisibility), default=enums.ListVisibility.OPEN, nullable=False)
visibility_censures = db.Column(Enum(enums.ListVisibility), default=enums.ListVisibility.OPEN, nullable=False)
visibility_hesitations = db.Column(Enum(enums.ListVisibility), default=enums.ListVisibility.OPEN, nullable=False)
approvals = db.relationship("Endorsement", back_populates="approving_instance", cascade="all, delete-orphan", foreign_keys=[Endorsement.approving_id]) approvals = db.relationship("Endorsement", back_populates="approving_instance", cascade="all, delete-orphan", foreign_keys=[Endorsement.approving_id])
endorsements = db.relationship("Endorsement", back_populates="endorsed_instance", cascade="all, delete-orphan", foreign_keys=[Endorsement.endorsed_id]) endorsements = db.relationship("Endorsement", back_populates="endorsed_instance", cascade="all, delete-orphan", foreign_keys=[Endorsement.endorsed_id])
@ -166,3 +168,12 @@ class Instance(db.Model):
def is_claimed(self): def is_claimed(self):
return len(self.admins) > 0 return len(self.admins) > 0
def is_endorsing(self,instance):
return instance in self.approvals
def is_censuring(self,instance):
return instance in self.censures_given
def is_hesitating(self,instance):
return instance in self.hesitations_given

View File

@ -1,4 +1,4 @@
FEDISEER_VERSION = "0.15.0" FEDISEER_VERSION = "0.16.0"
SUPPORTED_SOFTWARE = { SUPPORTED_SOFTWARE = {
"lemmy", "lemmy",
"mastodon", "mastodon",

View File

@ -16,3 +16,8 @@ class ReportActivity(enum.Enum):
class PMProxy(enum.Enum): class PMProxy(enum.Enum):
NONE = 0 NONE = 0
MASTODON = 1 MASTODON = 1
class ListVisibility(enum.Enum):
OPEN = 0
ENDORSED = 1
PRIVATE = 2

View File

@ -0,0 +1,4 @@
CREATE TYPE listvisibility AS ENUM ('OPEN','ENDORSED','PRIVATE');
ALTER TABLE instances ADD COLUMN visibility_endorsements listvisibility default 'OPEN';
ALTER TABLE instances ADD COLUMN visibility_censures listvisibility default 'OPEN';
ALTER TABLE instances ADD COLUMN visibility_hesitations listvisibility default 'OPEN';