Add/Remove endorsements
parent
c21caa6d08
commit
51bd6e83bf
|
@ -25,6 +25,7 @@ class Models:
|
|||
'csv': fields.String(description="The suspicious domains as a csv."),
|
||||
})
|
||||
self.response_model_instances = api.model('InstanceDetails', {
|
||||
'id': fields.Integer(description="The instance id"),
|
||||
'domain': fields.String(description="The instance domain"),
|
||||
'open_registrations': fields.Boolean(description="The instance uptime pct. 100% and thousand of users is unlikely"),
|
||||
'email_verify': fields.Boolean(description="The amount of local posts in that instance"),
|
||||
|
@ -32,8 +33,8 @@ class Models:
|
|||
'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."),
|
||||
})
|
||||
self.response_model_model_Whitelist_get = api.model('Instances', {
|
||||
self.response_model_model_Whitelist_get = api.model('WhitelistedInstances', {
|
||||
'instances': fields.List(fields.Nested(self.response_model_instances)),
|
||||
'domains': fields.List(fields.String(description="The instance domains as a list.")),
|
||||
'csv': fields.String(description="The instance domains as a csv."),
|
||||
})
|
||||
})
|
|
@ -1,5 +1,9 @@
|
|||
import overseer.apis.v1.base as base
|
||||
import overseer.apis.v1.endorsements as endorsements
|
||||
from overseer.apis.v1.base import api
|
||||
|
||||
api.add_resource(base.Suspicions, "/instances")
|
||||
api.add_resource(base.Whitelist, "/whitelist")
|
||||
api.add_resource(base.WhitelistDomain, "/whitelist/<string:domain>")
|
||||
api.add_resource(endorsements.Endorsements, "/endorsements/<string:domain>")
|
||||
api.add_resource(endorsements.Approvals, "/approvals/<string:domain>")
|
||||
|
|
|
@ -54,8 +54,8 @@ class Suspicions(Resource):
|
|||
class Whitelist(Resource):
|
||||
get_parser = reqparse.RequestParser()
|
||||
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("endorsements", required=False, default=1, type=int, help="Limit to this amount of endorsements of more", location="args")
|
||||
get_parser.add_argument("domain", required=False, type=str, help="Filter by instance domain", location="args")
|
||||
get_parser.add_argument("endorsements", required=False, default=0, type=int, help="Limit to this amount of endorsements of more", location="args")
|
||||
get_parser.add_argument("guarantors", required=False, default=1, type=int, help="Limit to this amount of guarantors of more", 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")
|
||||
|
||||
|
@ -67,7 +67,7 @@ class Whitelist(Resource):
|
|||
'''
|
||||
self.args = self.get_parser.parse_args()
|
||||
instance_details = []
|
||||
for instance in database.get_all_instances():
|
||||
for instance in database.get_all_instances(self.args.endorsements,self.args.guarantors):
|
||||
instance_details.append(instance.get_details())
|
||||
if self.args.csv:
|
||||
return {"csv": ",".join([instance["domain"] for instance in instance_details])},200
|
||||
|
@ -84,6 +84,7 @@ class Whitelist(Resource):
|
|||
|
||||
@api.expect(put_parser)
|
||||
@api.marshal_with(models.response_model_instances, code=200, description='Instances')
|
||||
@api.response(400, 'Bad Request', models.response_model_error)
|
||||
def put(self):
|
||||
'''Register a new instance to the overseer
|
||||
An instance account has to exist in the overseer lemmy instance
|
||||
|
@ -117,15 +118,17 @@ class Whitelist(Resource):
|
|||
|
||||
@api.expect(patch_parser)
|
||||
@api.marshal_with(models.response_model_instances, code=200, description='Instances', skip_none=True)
|
||||
@api.response(401, 'Invalid API Key', models.response_model_error)
|
||||
@api.response(403, 'Instance Not Registered', models.response_model_error)
|
||||
def patch(self):
|
||||
'''Regenerate API key for 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 your overctrl.dbzer0.com account")
|
||||
instance = database.find_authenticated_instance(self.args.domain, self.args.apikey)
|
||||
instance = database.find_instance_by_api_key(self.args.apikey)
|
||||
if not instance:
|
||||
raise e.BadRequest(f"No Instance found matching provided API key and domain. Have you remembered to register it?")
|
||||
raise e.Forbidden(f"No Instance found matching provided API key and domain. Have you remembered to register it?")
|
||||
if self.args.regenerate_key:
|
||||
new_key = pm_new_api_key(self.args.domain)
|
||||
instance.api_key = hash_api_key(new_key)
|
||||
|
@ -140,6 +143,9 @@ class Whitelist(Resource):
|
|||
|
||||
@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):
|
||||
'''Delete instance from overseer
|
||||
'''
|
||||
|
@ -154,4 +160,22 @@ class Whitelist(Resource):
|
|||
db.session.delete(instance)
|
||||
db.session.commit()
|
||||
logger.warning(f"{self.args.domain} deleted")
|
||||
return {"message":'OK'}, 200
|
||||
return {"message":'Changed'}, 200
|
||||
|
||||
|
||||
|
||||
class WhitelistDomain(Resource):
|
||||
get_parser = reqparse.RequestParser()
|
||||
get_parser.add_argument("Client-Agent", default="unknown:0:unknown", type=str, required=False, help="The client name and version.", location="headers")
|
||||
|
||||
@api.expect(get_parser)
|
||||
@cache.cached(timeout=10, query_string=True)
|
||||
@api.marshal_with(models.response_model_instances, code=200, description='Instances')
|
||||
def get(self, domain):
|
||||
'''Display info about a specific instance
|
||||
'''
|
||||
self.args = self.get_parser.parse_args()
|
||||
instance = database.find_instance_by_domain(domain)
|
||||
if not instance:
|
||||
raise e.NotFound(f"No Instance found matching provided domain. Have you remembered to register it?")
|
||||
return instance.get_details(),200
|
|
@ -0,0 +1,122 @@
|
|||
from overseer.apis.v1.base import *
|
||||
from overseer.classes.instance import Endorsement
|
||||
|
||||
class Approvals(Resource):
|
||||
get_parser = reqparse.RequestParser()
|
||||
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("domains", required=False, type=bool, help="Set to true to return just the domains as a list. Mutually exclusive with csv", location="args")
|
||||
|
||||
@api.expect(get_parser)
|
||||
@cache.cached(timeout=10, query_string=True)
|
||||
@api.marshal_with(models.response_model_model_Whitelist_get, code=200, description='Instances', skip_none=True)
|
||||
@api.response(404, 'Instance not registered', models.response_model_error)
|
||||
def get(self, domain):
|
||||
'''Display all endorsements given by a specific domain
|
||||
'''
|
||||
self.args = self.get_parser.parse_args()
|
||||
instance = database.find_instance_by_domain(domain)
|
||||
if not instance:
|
||||
raise e.NotFound(f"No Instance found matching provided domain. Have you remembered to register it?")
|
||||
instance_details = []
|
||||
for instance in database.get_all_endorsed_instances_by_approving_id(instance.id):
|
||||
instance_details.append(instance.get_details())
|
||||
if self.args.csv:
|
||||
return {"csv": ",".join([instance["domain"] for instance in instance_details])},200
|
||||
if self.args.domains:
|
||||
return {"domains": [instance["domain"] for instance in instance_details]},200
|
||||
return {"instances": instance_details},200
|
||||
|
||||
class Endorsements(Resource):
|
||||
get_parser = reqparse.RequestParser()
|
||||
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("domains", required=False, type=bool, help="Set to true to return just the domains as a list. Mutually exclusive with csv", location="args")
|
||||
|
||||
@api.expect(get_parser)
|
||||
@cache.cached(timeout=10, query_string=True)
|
||||
@api.marshal_with(models.response_model_model_Whitelist_get, code=200, description='Instances', skip_none=True)
|
||||
@api.response(404, 'Instance not registered', models.response_model_error)
|
||||
def get(self, domain):
|
||||
'''Display all endorsements given by a specific domain
|
||||
'''
|
||||
self.args = self.get_parser.parse_args()
|
||||
instance = database.find_instance_by_domain(domain)
|
||||
if not instance:
|
||||
raise e.NotFound(f"No Instance found matching provided domain. Have you remembered to register it?")
|
||||
instance_details = []
|
||||
for instance in database.get_all_approving_instances_by_endorsed_id(instance.id):
|
||||
instance_details.append(instance.get_details())
|
||||
if self.args.csv:
|
||||
return {"csv": ",".join([instance["domain"] for instance in instance_details])},200
|
||||
if self.args.domains:
|
||||
return {"domains": [instance["domain"] for instance in instance_details]},200
|
||||
return {"instances": instance_details},200
|
||||
|
||||
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")
|
||||
|
||||
|
||||
@api.expect(put_parser)
|
||||
@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 put(self, domain):
|
||||
'''Endorse an instance
|
||||
'''
|
||||
self.args = self.put_parser.parse_args()
|
||||
if not self.args.apikey:
|
||||
raise e.Unauthorized("You must provide the API key that was PM'd to your overctrl.dbzer0.com 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?")
|
||||
if len(instance.guarantors) == 0:
|
||||
raise e.Forbidden("Only guaranteed instances can endorse others.")
|
||||
if instance.domain == domain:
|
||||
raise e.BadRequest("Nice try, but you can't endorse yourself.")
|
||||
target_instance = database.find_instance_by_domain(domain=domain)
|
||||
if not target_instance:
|
||||
raise e.BadRequest("Instance to endorse not found")
|
||||
if database.get_endorsement(target_instance.id,instance.id):
|
||||
return {"message":'OK'}, 200
|
||||
new_endorsement = Endorsement(
|
||||
approving_id=instance.id,
|
||||
endorsed_id=target_instance.id,
|
||||
)
|
||||
db.session.add(new_endorsement)
|
||||
db.session.commit()
|
||||
logger.info(f"{instance.domain} Endorsed {domain}")
|
||||
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")
|
||||
|
||||
@api.expect(delete_parser)
|
||||
@api.marshal_with(models.response_model_simple_response, code=200, description='Withdraw Instance Endorsement')
|
||||
@api.response(400, 'Bad Request', models.response_model_error)
|
||||
@api.response(401, 'Invalid API Key', models.response_model_error)
|
||||
@api.response(404, 'Instance not registered', models.response_model_error)
|
||||
def delete(self,domain):
|
||||
'''Withdraw an instance endorsement
|
||||
'''
|
||||
self.args = self.delete_parser.parse_args()
|
||||
if not self.args.apikey:
|
||||
raise e.Unauthorized("You must provide the API key that was PM'd to your overctrl.dbzer0.com 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 withdraw endorsement not found")
|
||||
endorsement = database.get_endorsement(target_instance.id,instance.id)
|
||||
if not endorsement:
|
||||
return {"message":'OK'}, 200
|
||||
db.session.delete(endorsement)
|
||||
db.session.commit()
|
||||
logger.info(f"{instance.domain} Withdrew endorsement from {domain}")
|
||||
return {"message":'Changed'}, 200
|
|
@ -56,6 +56,7 @@ class Instance(db.Model):
|
|||
def get_details(self):
|
||||
guarantor = self.get_guarantor()
|
||||
ret_dict = {
|
||||
"id": self.id,
|
||||
"domain": self.domain,
|
||||
"open_registrations": self.open_registrations,
|
||||
"email_verify": self.email_verify,
|
||||
|
|
|
@ -1,16 +1,61 @@
|
|||
import time
|
||||
import uuid
|
||||
import json
|
||||
from loguru import logger
|
||||
from datetime import datetime, timedelta
|
||||
from sqlalchemy import func, or_, and_, not_, Boolean
|
||||
from sqlalchemy.orm import noload
|
||||
from overseer.flask import db, SQLITE_MODE
|
||||
from overseer.utils import hash_api_key
|
||||
from sqlalchemy.orm import joinedload
|
||||
from overseer.classes.instance import Instance, Endorsement
|
||||
|
||||
from overseer.classes.instance import Instance
|
||||
def get_all_instances(min_endorsements = 0, min_guarantors = 1):
|
||||
query = db.session.query(
|
||||
Instance
|
||||
).outerjoin(
|
||||
Instance.endorsements,
|
||||
Instance.guarantors,
|
||||
).options(
|
||||
joinedload(Instance.guarantors),
|
||||
joinedload(Instance.endorsements),
|
||||
).group_by(
|
||||
Instance.id
|
||||
).having(
|
||||
db.func.count(Instance.endorsements) >= min_endorsements,
|
||||
).having(
|
||||
db.func.count(Instance.guarantors) >= min_guarantors,
|
||||
)
|
||||
return query.all()
|
||||
|
||||
def get_all_instances():
|
||||
return db.session.query(Instance).all()
|
||||
|
||||
def get_all_endorsed_instances_by_approving_id(approving_id):
|
||||
query = db.session.query(
|
||||
Instance
|
||||
).outerjoin(
|
||||
Instance.endorsements,
|
||||
).options(
|
||||
joinedload(Instance.endorsements),
|
||||
).filter(
|
||||
Endorsement.approving_id == approving_id
|
||||
).group_by(
|
||||
Instance.id
|
||||
)
|
||||
return query.all()
|
||||
|
||||
def get_all_approving_instances_by_endorsed_id(endorsed_id):
|
||||
query = db.session.query(
|
||||
Instance
|
||||
).outerjoin(
|
||||
Instance.approvals,
|
||||
).options(
|
||||
joinedload(Instance.approvals),
|
||||
).filter(
|
||||
Endorsement.endorsed_id == endorsed_id
|
||||
).group_by(
|
||||
Instance.id
|
||||
)
|
||||
return query.all()
|
||||
|
||||
|
||||
def find_instance_by_api_key(api_key):
|
||||
|
@ -23,4 +68,11 @@ def find_instance_by_domain(domain):
|
|||
|
||||
def find_authenticated_instance(domain,api_key):
|
||||
instance = Instance.query.filter_by(domain=domain, api_key=hash_api_key(api_key)).first()
|
||||
return instance
|
||||
return instance
|
||||
|
||||
def get_endorsement(instance_id, endorsing_instance_id):
|
||||
query = Endorsement.query.filter_by(
|
||||
endorsed_id=instance_id,
|
||||
approving_id=endorsing_instance_id,
|
||||
)
|
||||
return query.first()
|
|
@ -12,7 +12,7 @@ overseer_lemmy_user = overctrl_lemmy.user.get(username=os.getenv('OVERSEER_LEMMY
|
|||
|
||||
def pm_new_api_key(domain: str):
|
||||
api_key = secrets.token_urlsafe(16)
|
||||
pm_content = f"The API Key for domain {domain} is {api_key}.\n\nUse this to perform operations on the overseer."
|
||||
pm_content = f"The API Key for domain {domain} is\n\n{api_key}\n\nUse this to perform operations on the overseer."
|
||||
domain_username = domain.replace(".", "_")
|
||||
domain_user = overctrl_lemmy.user.get(username=domain_username)
|
||||
if not domain_user:
|
||||
|
|
Loading…
Reference in New Issue