Feat: Instance flags (#36)
parent
3fb1647640
commit
85b19dc50c
|
@ -1,5 +1,9 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
# 0.18.0
|
||||||
|
|
||||||
|
* Added instance flags
|
||||||
|
|
||||||
# 0.17.1
|
# 0.17.1
|
||||||
|
|
||||||
* Prevent endorsement PMs being sent when visibility is private
|
* Prevent endorsement PMs being sent when visibility is private
|
||||||
|
|
|
@ -42,10 +42,15 @@ class Models:
|
||||||
'moderators': fields.Integer(required=False, default=None, description="The count of community moderators in this instance as reported by its admins."),
|
'moderators': fields.Integer(required=False, default=None, description="The count of community moderators in this instance as reported by its admins."),
|
||||||
'state': fields.String(required=True, enum=[e.name for e in enums.InstanceState], description="The state of the instance as seen from the fediseer."),
|
'state': fields.String(required=True, enum=[e.name for e in enums.InstanceState], description="The state of the instance as seen from the fediseer."),
|
||||||
})
|
})
|
||||||
|
self.response_model_flag_details = api.model('FlagDetails', {
|
||||||
|
'flag': fields.String(required=True, enum=[e.name for e in enums.InstanceFlags], description="The type of flag"),
|
||||||
|
'comment': fields.String(required=False, description="A comment explaining this flag", example="admin"),
|
||||||
|
})
|
||||||
self.response_model_instances_visibility = api.inherit('InstanceVisibilityDetails', self.response_model_instances, {
|
self.response_model_instances_visibility = api.inherit('InstanceVisibilityDetails', self.response_model_instances, {
|
||||||
'visibility_endorsements': fields.String(required=True, enum=[e.name for e in enums.ListVisibility], description="If OPEN, this instance allows anyone to read this instance's endorsements. When set to ENDORSED, only endorsed instances can see their endorsements. If set to PRIVATE allow this instance's own admins can see their endorsements."),
|
'visibility_endorsements': fields.String(required=True, enum=[e.name for e in enums.ListVisibility], description="If OPEN, this instance allows anyone to read this instance's endorsements. When set to ENDORSED, only endorsed instances can see their endorsements. If set to PRIVATE allow this instance's own admins can see their endorsements."),
|
||||||
'visibility_censures': fields.String(required=True, enum=[e.name for e in enums.ListVisibility], description="If OPEN, this instance allows anyone to read this instance's censures. When set to ENDORSED, only endorsed instances can see their censures. If set to PRIVATE allow this instance's own admins can see their censures."),
|
'visibility_censures': fields.String(required=True, enum=[e.name for e in enums.ListVisibility], description="If OPEN, this instance allows anyone to read this instance's censures. When set to ENDORSED, only endorsed instances can see their censures. If set to PRIVATE allow this instance's own admins can see their censures."),
|
||||||
'visibility_hesitations': fields.String(required=True, enum=[e.name for e in enums.ListVisibility], description="If OPEN, this instance allows anyone to read this instance's hesitations. When set to ENDORSED, only endorsed instances can see their hesitations. If set to PRIVATE allow this instance's own admins can see their hesitations."),
|
'visibility_hesitations': fields.String(required=True, enum=[e.name for e in enums.ListVisibility], description="If OPEN, this instance allows anyone to read this instance's hesitations. When set to ENDORSED, only endorsed instances can see their hesitations. If set to PRIVATE allow this instance's own admins can see their hesitations."),
|
||||||
|
'flags': fields.List(fields.Nested(self.response_model_flag_details)),
|
||||||
})
|
})
|
||||||
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_visibility)),
|
'instances': fields.List(fields.Nested(self.response_model_instances_visibility)),
|
||||||
|
@ -126,3 +131,7 @@ class Models:
|
||||||
'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_flag_modify = api.model('FlagModify', {
|
||||||
|
'flag': fields.String(required=True, enum=[e.name for e in enums.InstanceFlags], description="The type of flag to apply"),
|
||||||
|
'comment': fields.String(max_length=255, required=False, description="A comment explaining this flag", example="reasons"),
|
||||||
|
})
|
||||||
|
|
|
@ -9,6 +9,7 @@ import fediseer.apis.v1.activitypub as activitypub
|
||||||
import fediseer.apis.v1.badges as badges
|
import fediseer.apis.v1.badges as badges
|
||||||
import fediseer.apis.v1.find as find
|
import fediseer.apis.v1.find as find
|
||||||
import fediseer.apis.v1.report as report
|
import fediseer.apis.v1.report as report
|
||||||
|
import fediseer.apis.v1.admin as admin
|
||||||
from fediseer.apis.v1.base import api
|
from fediseer.apis.v1.base import api
|
||||||
|
|
||||||
api.add_resource(base.Suspicions, "/instances")
|
api.add_resource(base.Suspicions, "/instances")
|
||||||
|
@ -28,4 +29,5 @@ api.add_resource(guarantees.Guarantors, "/guarantors/<string:domain>")
|
||||||
api.add_resource(guarantees.Guarantees, "/guarantees/<string:domain>")
|
api.add_resource(guarantees.Guarantees, "/guarantees/<string:domain>")
|
||||||
api.add_resource(badges.GuaranteeBadge, "/badges/guarantees/<string:domain>.svg")
|
api.add_resource(badges.GuaranteeBadge, "/badges/guarantees/<string:domain>.svg")
|
||||||
api.add_resource(badges.EndorsementBadge, "/badges/endorsements/<string:domain>.svg")
|
api.add_resource(badges.EndorsementBadge, "/badges/endorsements/<string:domain>.svg")
|
||||||
|
api.add_resource(admin.Flag, "/admin/flags/<string:domain>")
|
||||||
api.add_resource(report.Report, "/reports")
|
api.add_resource(report.Report, "/reports")
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
from fediseer.apis.v1.base import *
|
||||||
|
from fediseer.messaging import activitypub_pm
|
||||||
|
from fediseer import enums
|
||||||
|
from fediseer.classes.reports import Report
|
||||||
|
from fediseer.register import ensure_instance_registered
|
||||||
|
from fediseer.classes.instance import InstanceFlag
|
||||||
|
|
||||||
|
class Flag(Resource):
|
||||||
|
|
||||||
|
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("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
|
||||||
|
put_parser.add_argument("comment", required=False, type=str, help="provide a reasoning for this flag", location="json")
|
||||||
|
put_parser.add_argument("flag", required=True, type=str, help="The flag to apply", location="json")
|
||||||
|
|
||||||
|
|
||||||
|
@api.expect(put_parser,models.input_flag_modify, validate=True)
|
||||||
|
@api.marshal_with(models.response_model_simple_response, code=200, description='Action Result')
|
||||||
|
@api.response(400, 'Bad Request', models.response_model_error)
|
||||||
|
@api.response(401, 'Invalid API Key', models.response_model_error)
|
||||||
|
@api.response(403, 'Access Denied', models.response_model_error)
|
||||||
|
|
||||||
|
def put(self, domain):
|
||||||
|
'''Flag an instance
|
||||||
|
'''
|
||||||
|
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 the admin account")
|
||||||
|
user = database.find_user_by_api_key(self.args.apikey)
|
||||||
|
if not user:
|
||||||
|
raise e.Forbidden("Only a fediseer admin can modify instance flags")
|
||||||
|
if user.account != "@fediseer@fediseer.com":
|
||||||
|
raise e.Forbidden("Only a fediseer admin can modify instance flags")
|
||||||
|
admin_instance = database.find_instance_by_user(user)
|
||||||
|
target_instance, instance_info = ensure_instance_registered(domain)
|
||||||
|
flag = enums.InstanceFlags[self.args.flag]
|
||||||
|
if database.instance_has_flag(target_instance.id,flag):
|
||||||
|
return {"message": "OK"},200
|
||||||
|
new_flag = InstanceFlag(
|
||||||
|
instance_id = target_instance.id,
|
||||||
|
flag = flag,
|
||||||
|
comment = self.args.comment,
|
||||||
|
)
|
||||||
|
db.session.add(new_flag)
|
||||||
|
if flag == enums.InstanceFlags.RESTRICTED and not database.instance_has_flag(target_instance.id,enums.InstanceFlags.MUTED):
|
||||||
|
muted_flag = InstanceFlag(
|
||||||
|
instance_id = target_instance.id,
|
||||||
|
flag = enums.InstanceFlags.MUTED,
|
||||||
|
comment = "Restricted with reason: " + self.args.comment,
|
||||||
|
)
|
||||||
|
db.session.add(muted_flag)
|
||||||
|
# Sactioned instances get no visibility
|
||||||
|
if flag in [enums.InstanceFlags.MUTED,enums.InstanceFlags.RESTRICTED]:
|
||||||
|
target_instance.visibility_censures = enums.ListVisibility.PRIVATE
|
||||||
|
target_instance.visibility_endorsements = enums.ListVisibility.PRIVATE
|
||||||
|
target_instance.visibility_hesitations = enums.ListVisibility.PRIVATE
|
||||||
|
new_report = Report(
|
||||||
|
source_domain=admin_instance.domain,
|
||||||
|
target_domain=target_instance.domain,
|
||||||
|
report_type=enums.ReportType.FLAG,
|
||||||
|
report_activity=enums.ReportActivity.ADDED,
|
||||||
|
)
|
||||||
|
db.session.add(new_report)
|
||||||
|
db.session.commit()
|
||||||
|
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("comment", required=False, type=str, help="provide a reasoning for this flag", location="json")
|
||||||
|
patch_parser.add_argument("flag", required=False, type=str, help="The flag to apply", location="json")
|
||||||
|
|
||||||
|
|
||||||
|
@api.expect(patch_parser,models.input_flag_modify, validate=True)
|
||||||
|
@api.marshal_with(models.response_model_simple_response, code=200, description='Action Result')
|
||||||
|
@api.response(400, 'Bad Request', models.response_model_error)
|
||||||
|
@api.response(401, 'Invalid API Key', models.response_model_error)
|
||||||
|
@api.response(403, 'Access Denied', models.response_model_error)
|
||||||
|
@api.response(404, 'Instance or flag not found', models.response_model_error)
|
||||||
|
def patch(self, domain):
|
||||||
|
'''Modify an instance's flag
|
||||||
|
'''
|
||||||
|
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 the admin account")
|
||||||
|
user = database.find_user_by_api_key(self.args.apikey)
|
||||||
|
if not user:
|
||||||
|
raise e.Forbidden("Only a fediseer admin can modify instance flags")
|
||||||
|
if user.account != "@fediseer@fediseer.com":
|
||||||
|
raise e.Forbidden("Only a fediseer admin can modify instance flags")
|
||||||
|
admin_instance = database.find_instance_by_user(user)
|
||||||
|
target_instance, instance_info = ensure_instance_registered(domain)
|
||||||
|
flag = enums.InstanceFlags[self.args.flag]
|
||||||
|
existing_flag = database.get_instance_flag(target_instance.id,flag)
|
||||||
|
if not existing_flag:
|
||||||
|
raise e.NotFound(f"{flag.name} not found in {domain}")
|
||||||
|
if existing_flag.comment == self.args.comment:
|
||||||
|
return {"message": "OK"},200
|
||||||
|
existing_flag.comment = self.args.comment,
|
||||||
|
new_report = Report(
|
||||||
|
source_domain=admin_instance.domain,
|
||||||
|
target_domain=target_instance.domain,
|
||||||
|
report_type=enums.ReportType.FLAG,
|
||||||
|
report_activity=enums.ReportActivity.MODIFIED,
|
||||||
|
)
|
||||||
|
db.session.add(new_report)
|
||||||
|
db.session.commit()
|
||||||
|
return {"message": "Changed"},200
|
||||||
|
|
||||||
|
|
||||||
|
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("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
|
||||||
|
delete_parser.add_argument("flag", required=False, type=str, help="The flag to delete", location="json")
|
||||||
|
|
||||||
|
|
||||||
|
@api.expect(delete_parser)
|
||||||
|
@api.marshal_with(models.response_model_simple_response, code=200, description='Instances', skip_none=True)
|
||||||
|
@api.response(400, 'Bad Request', models.response_model_error)
|
||||||
|
@api.response(401, 'Invalid API Key', models.response_model_error)
|
||||||
|
@api.response(403, 'Forbidden', models.response_model_error)
|
||||||
|
def delete(self, domain):
|
||||||
|
'''Delete an instance's flag
|
||||||
|
'''
|
||||||
|
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 the admin account")
|
||||||
|
user = database.find_user_by_api_key(self.args.apikey)
|
||||||
|
if not user:
|
||||||
|
raise e.Forbidden("Only a fediseer admin can delete instance flags")
|
||||||
|
if user.account != "@fediseer@fediseer.com":
|
||||||
|
raise e.Forbidden("Only a fediseer admin can delete instance flags")
|
||||||
|
admin_instance = database.find_instance_by_user(user)
|
||||||
|
target_instance, instance_info = ensure_instance_registered(domain)
|
||||||
|
flag = enums.InstanceFlags[self.args.flag]
|
||||||
|
existing_flag = database.get_instance_flag(target_instance.id,flag)
|
||||||
|
if not existing_flag:
|
||||||
|
return {"message": "OK"},200
|
||||||
|
db.session.delete(existing_flag)
|
||||||
|
new_report = Report(
|
||||||
|
source_domain=admin_instance.domain,
|
||||||
|
target_domain=target_instance.domain,
|
||||||
|
report_type=enums.ReportType.FLAG,
|
||||||
|
report_activity=enums.ReportActivity.DELETED,
|
||||||
|
)
|
||||||
|
db.session.add(new_report)
|
||||||
|
db.session.commit()
|
||||||
|
return {"message": "Changed"},200
|
|
@ -165,7 +165,7 @@ class Censures(Resource):
|
||||||
@api.marshal_with(models.response_model_simple_response, code=200, description='Censure Instance')
|
@api.marshal_with(models.response_model_simple_response, code=200, description='Censure 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)
|
||||||
@api.response(403, 'Not Guaranteed', models.response_model_error)
|
@api.response(403, 'Access Denied', models.response_model_error)
|
||||||
@api.response(404, 'Instance not registered', models.response_model_error)
|
@api.response(404, 'Instance not registered', models.response_model_error)
|
||||||
def put(self, domain):
|
def put(self, domain):
|
||||||
'''Censure an instance
|
'''Censure an instance
|
||||||
|
@ -179,6 +179,8 @@ class Censures(Resource):
|
||||||
raise e.NotFound(f"No Instance found matching provided API key and domain. Have you remembered to register it?")
|
raise e.NotFound(f"No Instance found matching provided API key and domain. Have you remembered to register it?")
|
||||||
if len(instance.guarantors) == 0:
|
if len(instance.guarantors) == 0:
|
||||||
raise e.Forbidden("Only guaranteed instances can censure others.")
|
raise e.Forbidden("Only guaranteed instances can censure others.")
|
||||||
|
if database.instance_has_flag(instance.id,enums.InstanceFlags.RESTRICTED):
|
||||||
|
raise e.Forbidden("You cannot take this action as your instance is restricted")
|
||||||
if instance.domain == domain:
|
if instance.domain == domain:
|
||||||
raise e.BadRequest("You're a mad lad, but you can't censure yourself.")
|
raise e.BadRequest("You're a mad lad, but you can't censure yourself.")
|
||||||
if database.has_too_many_actions_per_min(instance.domain):
|
if database.has_too_many_actions_per_min(instance.domain):
|
||||||
|
|
|
@ -144,7 +144,7 @@ class Endorsements(Resource):
|
||||||
@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)
|
||||||
@api.response(403, 'Not Guaranteed', models.response_model_error)
|
@api.response(403, 'Access Denied', models.response_model_error)
|
||||||
@api.response(404, 'Instance not registered', models.response_model_error)
|
@api.response(404, 'Instance not registered', models.response_model_error)
|
||||||
def put(self, domain):
|
def put(self, domain):
|
||||||
'''Endorse an instance
|
'''Endorse an instance
|
||||||
|
@ -158,6 +158,8 @@ class Endorsements(Resource):
|
||||||
raise e.NotFound(f"No Instance found matching provided API key and domain. Have you remembered to register it?")
|
raise e.NotFound(f"No Instance found matching provided API key and domain. Have you remembered to register it?")
|
||||||
if len(instance.guarantors) == 0:
|
if len(instance.guarantors) == 0:
|
||||||
raise e.Forbidden("Only guaranteed instances can endorse others.")
|
raise e.Forbidden("Only guaranteed instances can endorse others.")
|
||||||
|
if database.instance_has_flag(instance.id,enums.InstanceFlags.RESTRICTED):
|
||||||
|
raise e.Forbidden("You cannot take this action as your instance is restricted")
|
||||||
if instance.domain == domain:
|
if instance.domain == domain:
|
||||||
raise e.BadRequest("Nice try, but you can't endorse yourself.")
|
raise e.BadRequest("Nice try, but you can't endorse yourself.")
|
||||||
if database.has_too_many_actions_per_min(instance.domain):
|
if database.has_too_many_actions_per_min(instance.domain):
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from fediseer.apis.v1.base import *
|
from fediseer.apis.v1.base import *
|
||||||
from fediseer.classes.instance import Guarantee, RejectionRecord, Solicitation
|
from fediseer.classes.instance import Guarantee, RejectionRecord, Solicitation, InstanceFlag
|
||||||
from fediseer.classes.reports import Report
|
from fediseer.classes.reports import Report
|
||||||
from fediseer import enums
|
from fediseer import enums
|
||||||
from fediseer.register import ensure_instance_registered
|
from fediseer.register import ensure_instance_registered
|
||||||
|
@ -69,7 +69,7 @@ class Guarantees(Resource):
|
||||||
@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)
|
||||||
@api.response(403, 'Instance Not Guaranteed or Tartget instance Guaranteed by others', models.response_model_error)
|
@api.response(403, 'Access Denied', models.response_model_error)
|
||||||
@api.response(404, 'Instance not registered', models.response_model_error)
|
@api.response(404, 'Instance not registered', models.response_model_error)
|
||||||
def put(self, domain):
|
def put(self, domain):
|
||||||
'''Guarantee an instance
|
'''Guarantee an instance
|
||||||
|
@ -87,6 +87,8 @@ class Guarantees(Resource):
|
||||||
raise e.Forbidden("Only guaranteed instances can guarantee others.")
|
raise e.Forbidden("Only guaranteed instances can guarantee others.")
|
||||||
if len(instance.guarantees) >= 20 and instance.id != 0:
|
if len(instance.guarantees) >= 20 and instance.id != 0:
|
||||||
raise e.Forbidden("You cannot guarantee for more than 20 instances")
|
raise e.Forbidden("You cannot guarantee for more than 20 instances")
|
||||||
|
if database.instance_has_flag(instance.id,enums.InstanceFlags.RESTRICTED):
|
||||||
|
raise e.Forbidden("You cannot take this action as your instance is restricted")
|
||||||
if database.has_too_many_actions_per_min(instance.domain):
|
if database.has_too_many_actions_per_min(instance.domain):
|
||||||
raise e.TooManyRequests("Your instance is doing more than 20 actions per minute. Please slow down.")
|
raise e.TooManyRequests("Your instance is doing more than 20 actions per minute. Please slow down.")
|
||||||
unbroken_chain, chainbreaker = database.has_unbroken_chain(instance.id)
|
unbroken_chain, chainbreaker = database.has_unbroken_chain(instance.id)
|
||||||
|
@ -105,6 +107,13 @@ class Guarantees(Resource):
|
||||||
guarantor_id=instance.id,
|
guarantor_id=instance.id,
|
||||||
)
|
)
|
||||||
db.session.add(new_guarantee)
|
db.session.add(new_guarantee)
|
||||||
|
if database.instance_has_flag(instance.id,enums.InstanceFlags.MUTED):
|
||||||
|
muted_flag = InstanceFlag(
|
||||||
|
instance_id=target_instance.id,
|
||||||
|
flag=enums.InstanceFlags.MUTED,
|
||||||
|
comment=f"Inherited from guarantor {target_instance.domain}",
|
||||||
|
)
|
||||||
|
db.session.add(muted_flag)
|
||||||
database.delete_all_solicitation_by_source(target_instance.id)
|
database.delete_all_solicitation_by_source(target_instance.id)
|
||||||
new_report = Report(
|
new_report = Report(
|
||||||
source_domain=instance.domain,
|
source_domain=instance.domain,
|
||||||
|
|
|
@ -151,7 +151,7 @@ class Hesitations(Resource):
|
||||||
@api.marshal_with(models.response_model_simple_response, code=200, description='Mistrust Instance')
|
@api.marshal_with(models.response_model_simple_response, code=200, description='Mistrust 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)
|
||||||
@api.response(403, 'Not Guaranteed', models.response_model_error)
|
@api.response(403, 'Access Denied', models.response_model_error)
|
||||||
@api.response(404, 'Instance not registered', models.response_model_error)
|
@api.response(404, 'Instance not registered', models.response_model_error)
|
||||||
def put(self, domain):
|
def put(self, domain):
|
||||||
'''Hesitate against an instance
|
'''Hesitate against an instance
|
||||||
|
@ -165,6 +165,8 @@ class Hesitations(Resource):
|
||||||
raise e.NotFound(f"No Instance found matching provided API key and domain. Have you remembered to register it?")
|
raise e.NotFound(f"No Instance found matching provided API key and domain. Have you remembered to register it?")
|
||||||
if len(instance.guarantors) == 0:
|
if len(instance.guarantors) == 0:
|
||||||
raise e.Forbidden("Only guaranteed instances can hesitation others.")
|
raise e.Forbidden("Only guaranteed instances can hesitation others.")
|
||||||
|
if database.instance_has_flag(instance.id,enums.InstanceFlags.RESTRICTED):
|
||||||
|
raise e.Forbidden("You cannot take this action as your instance is restricted")
|
||||||
if instance.domain == domain:
|
if instance.domain == domain:
|
||||||
raise e.BadRequest("You're a mad lad, but you can't hesitation yourself.")
|
raise e.BadRequest("You're a mad lad, but you can't hesitation yourself.")
|
||||||
if database.has_too_many_actions_per_min(instance.domain):
|
if database.has_too_many_actions_per_min(instance.domain):
|
||||||
|
|
|
@ -159,7 +159,7 @@ class WhitelistDomain(Resource):
|
||||||
@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, 'Access Denied', models.response_model_error)
|
@api.response(403, 'Access Denied', models.response_model_error)
|
||||||
@api.response(403, 'Instance not claimed', models.response_model_error)
|
@api.response(404, 'Instance not claimed', models.response_model_error)
|
||||||
def patch(self, domain):
|
def patch(self, domain):
|
||||||
'''Regenerate API key for instance
|
'''Regenerate API key for instance
|
||||||
'''
|
'''
|
||||||
|
@ -174,7 +174,7 @@ class WhitelistDomain(Resource):
|
||||||
instance_to_reset = database.find_instance_by_domain(domain)
|
instance_to_reset = database.find_instance_by_domain(domain)
|
||||||
changed = False
|
changed = False
|
||||||
new_key = None
|
new_key = None
|
||||||
if requestor_instance != instance_to_reset and user.username != "fediseer":
|
if requestor_instance != instance_to_reset and user.account != "@fediseer@fediseer.com":
|
||||||
raise e.Forbidden("Only an instance admin can modify the instance")
|
raise e.Forbidden("Only an instance admin can modify the instance")
|
||||||
if self.args.sysadmins is not None and instance.sysadmins != self.args.sysadmins:
|
if self.args.sysadmins is not None and instance.sysadmins != self.args.sysadmins:
|
||||||
instance.sysadmins = self.args.sysadmins
|
instance.sysadmins = self.args.sysadmins
|
||||||
|
@ -183,6 +183,8 @@ 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:
|
||||||
|
if instance_to_reset is None:
|
||||||
|
raise e.NotFound(f"Instance {domain} has not been registered yet.")
|
||||||
proxy = enums.PMProxy[self.args.pm_proxy]
|
proxy = enums.PMProxy[self.args.pm_proxy]
|
||||||
if instance_to_reset.software == "lemmy" and proxy == enums.PMProxy.MASTODON:
|
if instance_to_reset.software == "lemmy" and proxy == enums.PMProxy.MASTODON:
|
||||||
raise e.BadRequest("I'm sorry Dave, I can't let you do that. Lemmy is not capable of receiving mastodon PMs.")
|
raise e.BadRequest("I'm sorry Dave, I can't let you do that. Lemmy is not capable of receiving mastodon PMs.")
|
||||||
|
@ -193,26 +195,32 @@ class WhitelistDomain(Resource):
|
||||||
if self.args.visibility_endorsements is not None:
|
if self.args.visibility_endorsements is not None:
|
||||||
visibility = enums.ListVisibility[self.args.visibility_endorsements]
|
visibility = enums.ListVisibility[self.args.visibility_endorsements]
|
||||||
if instance.visibility_endorsements != visibility:
|
if instance.visibility_endorsements != visibility:
|
||||||
|
if database.instance_has_flag(instance.id,enums.InstanceFlags.MUTED):
|
||||||
|
raise e.Forbidden("Muted instances cannot change their visibility away from private!")
|
||||||
instance.visibility_endorsements = visibility
|
instance.visibility_endorsements = visibility
|
||||||
changed = True
|
changed = True
|
||||||
if self.args.visibility_censures is not None:
|
if self.args.visibility_censures is not None:
|
||||||
visibility = enums.ListVisibility[self.args.visibility_censures]
|
visibility = enums.ListVisibility[self.args.visibility_censures]
|
||||||
if instance.visibility_censures != visibility:
|
if instance.visibility_censures != visibility:
|
||||||
|
if database.instance_has_flag(instance.id,enums.InstanceFlags.MUTED):
|
||||||
|
raise e.Forbidden("Muted instances cannot change their visibility away from private!")
|
||||||
instance.visibility_censures = visibility
|
instance.visibility_censures = visibility
|
||||||
changed = True
|
changed = True
|
||||||
if self.args.visibility_hesitations is not None:
|
if self.args.visibility_hesitations is not None:
|
||||||
visibility = enums.ListVisibility[self.args.visibility_hesitations]
|
visibility = enums.ListVisibility[self.args.visibility_hesitations]
|
||||||
if instance.visibility_hesitations != visibility:
|
if instance.visibility_hesitations != visibility:
|
||||||
|
if database.instance_has_flag(instance.id,enums.InstanceFlags.MUTED):
|
||||||
|
raise e.Forbidden("Muted instances cannot change their visibility away from private!")
|
||||||
instance.visibility_hesitations = visibility
|
instance.visibility_hesitations = visibility
|
||||||
changed = True
|
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.account == "@fediseer@fediseer.com":
|
||||||
requestor = user.username
|
requestor = user.username
|
||||||
instance_to_reset = database.find_instance_by_account(f"@{self.args.admin_username}@{domain}")
|
instance_to_reset = database.find_instance_by_account(f"@{self.args.admin_username}@{domain}")
|
||||||
if instance_to_reset is None:
|
if instance_to_reset is None:
|
||||||
raise e.NotFound(f"No admin '{self.args.admin_username}' found in instance {domain}. Have you remembered to claim it as that admin?")
|
raise e.NotFound(f"No admin '{self.args.admin_username}' found in instance {domain}. Have you remembered to claim it as that admin?")
|
||||||
if instance != instance_to_reset and user.username != "fediseer":
|
if instance != instance_to_reset and user.account != "@fediseer@fediseer.com":
|
||||||
raise e.BadRequest("Only other admins of the same instance or the fediseer can request API key reset for others.")
|
raise e.BadRequest("Only other admins of the same instance or the fediseer can request API key reset for others.")
|
||||||
|
|
||||||
instance = instance_to_reset
|
instance = instance_to_reset
|
||||||
|
|
|
@ -82,6 +82,16 @@ class Solicitation(db.Model):
|
||||||
target_instance = db.relationship("Instance", back_populates="solicitations_received", foreign_keys=[target_id])
|
target_instance = db.relationship("Instance", back_populates="solicitations_received", foreign_keys=[target_id])
|
||||||
created = db.Column(db.DateTime, default=datetime.utcnow, nullable=False, index=True)
|
created = db.Column(db.DateTime, default=datetime.utcnow, nullable=False, index=True)
|
||||||
|
|
||||||
|
class InstanceFlag(db.Model):
|
||||||
|
__tablename__ = "instance_flags"
|
||||||
|
__table_args__ = (UniqueConstraint('instance_id', 'flag', name='instance_flags_instance_id_flag'),)
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
comment = db.Column(db.String(255), unique=False, nullable=True, index=False)
|
||||||
|
flag = db.Column(Enum(enums.InstanceFlags), nullable=False, index=True)
|
||||||
|
instance_id = db.Column(db.Integer, db.ForeignKey("instances.id", ondelete="CASCADE"), nullable=False, index=True)
|
||||||
|
instance = db.relationship("Instance", back_populates="flags")
|
||||||
|
created = db.Column(db.DateTime, default=datetime.utcnow, nullable=False, index=True)
|
||||||
|
|
||||||
class Instance(db.Model):
|
class Instance(db.Model):
|
||||||
__tablename__ = "instances"
|
__tablename__ = "instances"
|
||||||
|
|
||||||
|
@ -117,6 +127,7 @@ class Instance(db.Model):
|
||||||
rejections = db.relationship("RejectionRecord", back_populates="rejector_instance", cascade="all, delete-orphan", foreign_keys=[RejectionRecord.rejector_id])
|
rejections = db.relationship("RejectionRecord", back_populates="rejector_instance", cascade="all, delete-orphan", foreign_keys=[RejectionRecord.rejector_id])
|
||||||
rejectors = db.relationship("RejectionRecord", back_populates="rejected_instance", cascade="all, delete-orphan", foreign_keys=[RejectionRecord.rejected_id])
|
rejectors = db.relationship("RejectionRecord", back_populates="rejected_instance", cascade="all, delete-orphan", foreign_keys=[RejectionRecord.rejected_id])
|
||||||
admins = db.relationship("Claim", back_populates="instance", cascade="all, delete-orphan")
|
admins = db.relationship("Claim", back_populates="instance", cascade="all, delete-orphan")
|
||||||
|
flags = db.relationship("InstanceFlag", back_populates="instance", cascade="all, delete-orphan")
|
||||||
|
|
||||||
def create(self):
|
def create(self):
|
||||||
db.session.add(self)
|
db.session.add(self)
|
||||||
|
@ -147,6 +158,12 @@ class Instance(db.Model):
|
||||||
ret_dict["visibility_endorsements"] = self.visibility_endorsements.name
|
ret_dict["visibility_endorsements"] = self.visibility_endorsements.name
|
||||||
ret_dict["visibility_censures"] = self.visibility_censures.name
|
ret_dict["visibility_censures"] = self.visibility_censures.name
|
||||||
ret_dict["visibility_hesitations"] = self.visibility_hesitations.name
|
ret_dict["visibility_hesitations"] = self.visibility_hesitations.name
|
||||||
|
ret_dict["flags"] = []
|
||||||
|
for flag in self.flags:
|
||||||
|
ret_dict["flags"].append({
|
||||||
|
"flag": flag.flag.name,
|
||||||
|
"comment": flag.comment
|
||||||
|
})
|
||||||
return ret_dict
|
return ret_dict
|
||||||
|
|
||||||
|
|
||||||
|
@ -197,4 +214,9 @@ class Instance(db.Model):
|
||||||
if self.poll_failures <= 30*POLLS_PER_DAY:
|
if self.poll_failures <= 30*POLLS_PER_DAY:
|
||||||
return enums.InstanceState.OFFLINE
|
return enums.InstanceState.OFFLINE
|
||||||
return enums.InstanceState.DECIMMISSIONED
|
return enums.InstanceState.DECIMMISSIONED
|
||||||
|
|
||||||
|
def has_flag(self, flag_enum):
|
||||||
|
for flag in self.flags:
|
||||||
|
if flag.flag == flag_enum:
|
||||||
|
return True
|
||||||
|
return False
|
|
@ -1,4 +1,4 @@
|
||||||
FEDISEER_VERSION = "0.17.1"
|
FEDISEER_VERSION = "0.18.0"
|
||||||
SUPPORTED_SOFTWARE = {
|
SUPPORTED_SOFTWARE = {
|
||||||
"lemmy",
|
"lemmy",
|
||||||
"mastodon",
|
"mastodon",
|
||||||
|
|
|
@ -8,7 +8,7 @@ from sqlalchemy.orm import noload
|
||||||
from fediseer.flask import db, SQLITE_MODE
|
from fediseer.flask import db, SQLITE_MODE
|
||||||
from fediseer.utils import hash_api_key
|
from fediseer.utils import hash_api_key
|
||||||
from sqlalchemy.orm import joinedload
|
from sqlalchemy.orm import joinedload
|
||||||
from fediseer.classes.instance import Instance, Endorsement, Guarantee, RejectionRecord, Censure, Hesitation, Solicitation
|
from fediseer.classes.instance import Instance, Endorsement, Guarantee, RejectionRecord, Censure, Hesitation, Solicitation, InstanceFlag
|
||||||
from fediseer.classes.user import Claim, User
|
from fediseer.classes.user import Claim, User
|
||||||
from fediseer.classes.reports import Report
|
from fediseer.classes.reports import Report
|
||||||
from fediseer import enums
|
from fediseer import enums
|
||||||
|
@ -459,4 +459,18 @@ def has_too_many_actions_per_min(source_domain):
|
||||||
).filter(
|
).filter(
|
||||||
Report.created > datetime.utcnow() - timedelta(minutes=1),
|
Report.created > datetime.utcnow() - timedelta(minutes=1),
|
||||||
)
|
)
|
||||||
return query.count() > 20
|
return query.count() > 20
|
||||||
|
|
||||||
|
def get_instance_flag(instance_id, flag_enum):
|
||||||
|
query = InstanceFlag.query.filter(
|
||||||
|
InstanceFlag.instance_id == instance_id,
|
||||||
|
InstanceFlag.flag == flag_enum,
|
||||||
|
)
|
||||||
|
return query.first()
|
||||||
|
|
||||||
|
def instance_has_flag(instance_id, flag_enum):
|
||||||
|
query = InstanceFlag.query.filter(
|
||||||
|
InstanceFlag.instance_id == instance_id,
|
||||||
|
InstanceFlag.flag == flag_enum,
|
||||||
|
)
|
||||||
|
return query.count() == 1
|
|
@ -7,6 +7,7 @@ class ReportType(enum.Enum):
|
||||||
HESITATION = 3
|
HESITATION = 3
|
||||||
CLAIM = 4
|
CLAIM = 4
|
||||||
SOLICITATION = 5
|
SOLICITATION = 5
|
||||||
|
FLAG = 6
|
||||||
|
|
||||||
class ReportActivity(enum.Enum):
|
class ReportActivity(enum.Enum):
|
||||||
ADDED = 0
|
ADDED = 0
|
||||||
|
@ -27,3 +28,7 @@ class InstanceState(enum.Enum):
|
||||||
UNREACHABLE = 1
|
UNREACHABLE = 1
|
||||||
OFFLINE = 2
|
OFFLINE = 2
|
||||||
DECIMMISSIONED = 3
|
DECIMMISSIONED = 3
|
||||||
|
|
||||||
|
class InstanceFlags(enum.Enum):
|
||||||
|
RESTRICTED = 0
|
||||||
|
MUTED = 1
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TYPE reporttype ADD VALUE 'FLAG';
|
Loading…
Reference in New Issue