manage: add ban adding and listing, middleware: improve ban checking and return json when on ap endpoints

This commit is contained in:
Izalia Mae 2021-09-22 18:49:06 -04:00
parent 4b0ac36ecc
commit 369ffe9e2a
5 changed files with 134 additions and 31 deletions

View file

@ -19,11 +19,11 @@ def cmd_actor(self, url):
def cmd_ban_list(self, types='domain'):
if types == 'domain':
return self.search('ban', handle=None)
return self.search('ban', handle=None, orderby='domain')
bans = []
for row in self.search('ban'):
for row in self.search('ban', orderby='domain'):
if row.handle:
bans.append(row)

View file

@ -10,20 +10,23 @@ def cmd_ban(self, handle=None, domain=None, reason=None):
if row:
if reason:
self.update(row=row, reason=reason)
row = self.update(row=row, reason=reason, return_row=True)
else:
logging.verbose('Banned user or instance already exists')
return
else:
self.insert('ban',
row = self.insert('ban',
handle = handle,
domain = domain,
reason = reason,
timestamp = datetime.now()
timestamp = datetime.now(),
return_row = True
)
return row
def cmd_actor(self, url, actor):
row = self.get.actor(url)

View file

@ -37,9 +37,11 @@ def fetch_actor(url):
error = None
if error:
logging.debug(f'Failed to fetch actor with error: {error}')
logging.debug(f'Failed to fetch actor "{url}" with error: {error}')
return
if not data or error:
if not data:
logging.verbose(f'Failed to fetch actor: {url}')
return
s.put.actor(url, data)

View file

@ -59,6 +59,22 @@ python3 -m add <actor or domain>: *
python3 -m remove <actor, inbox, or domain>:
Remove an instance and any retries associated with it from the database.
python3 -m uncia.manage ban <handle@domain or domain> [*reason]:
Ban a user or domain. A reason may optionally be specified.
python3 -m uncia.manage bans ["users" or "domains"]:
List the currently banned domains and users. A ban type ("users"/"domains")
may be specified to only list that type.
python3 -m uncia.manage rules [list]:
List the current rules.
python3 -m uncia.manage rules add <rule>:
Add a rule to the list.
python3 -m uncia.manage rules remove <rule number>:
Remove a rule from the list.
python3 -m uncia.manage config [key] [value]:
Gets or sets the config. Specify a key and value to set a config option.
Only specify a key to get the value of a specific option. Leave out the
@ -76,15 +92,6 @@ python3 -m uncia.manage accept <actor, inbox, or domain>: *
python3 -m uncia.manage deny <actor, inbox, or domain>: *
Reject a request.
python3 -m uncia.manage rules [list]:
List the current rules.
python3 -m uncia.manage rules add <rule>:
Add a rule to the list.
python3 -m uncia.manage rules remove <rule number>:
Remove a rule from the list.
python3 -m uncia.manage convert [pleroma or uncia]:
Convert the database and config of another relay. Tries to convert a
Pleroma Relay by default.
@ -249,6 +256,82 @@ python3 -m uncia.manage convert [pleroma or uncia]:
return f'Removed rule: {rule_text}'
def cmd_ban(self, string, *args):
handle = None
domain = None
data = string.split('@')
if len(data) == 1:
domain = data[0]
elif len(data) == 2:
handle = data[0]
domain = data[1]
elif len(data) == 3:
handle = data[1]
domain = data[2]
reason = None if not args else ' '.join(args)
with db.session as s:
row = s.put.ban(handle, domain, reason)
if row:
if handle:
return f'Banned {user}@{domain}'
return f'Banned {domain}'
else:
if handle:
return f'Already banned {user}@{domain}'
return f'Already banned {domain}'
def cmd_bans(self, type=None):
if type and type not in ['domains', 'users']:
return 'Ban type needs to be "domains" or "users"'
data = ''
user_bans = []
domain_bans = []
with db.session as s:
if type == 'domains':
domain_bans = s.get.ban_list('domain')
elif type == 'users':
user_bans = s.get.ban_list('user')
else:
domain_bans = s.get.ban_list('domain')
user_bans = s.get.ban_list('user')
if domain_bans:
data += 'Domain Bans:\n'
for ban in domain_bans:
if ban.reason:
data += f'- {ban.domain}: {ban.reason}\n'
else:
data += f'- {ban.domain}\n'
if user_bans:
data += '\nUser Bans:\n'
for ban in user_bans:
if ban.reason:
data += f'- {ban.handle}@{ban.domain}: {ban.reason}\n'
else:
data += f'- {ban.handle}@{ban.domain}\n'
return data or 'No banned domains or users yet'
def cmd_convert(self, relay='uncia'):
if relay.lower() == 'uncia':
return self.convert_uncia()

View file

@ -34,6 +34,25 @@ blocked_instances = [
]
def ban_check(s, request):
if any(map(s.get.ban, [None], [request.signature.domain, request.signature.top_domain])):
return True
if not request.actor:
return
handle = request.actor.preferredUsername
if s.get.ban(handle):
return True
if s.get.ban(handle, request.signature.domain):
return True
if s.get.ban(handle, request.signature.top_domain):
return True
class AuthCheck(MiddlewareBase):
attach = 'request'
@ -50,42 +69,38 @@ class AuthCheck(MiddlewareBase):
request.actor = None
if request.signature:
domain = request.signature.domain
top_domain = request.signature.top_domain
actor = request.signature.actor
request.instance = s.get.instance(request.signature.domain)
request.actor = fetch_actor(request.signature.actor)
if top_domain in blocked_instances:
if request.signature.top_domain in blocked_instances:
return response.text(f'This teapot kills fascists', status=418)
if any(map(s.get.ban, [None], [domain, top_domain])):
if ban_check(s, request):
return response.text('no', status=403)
request.instance = s.get.instance(domain)
request.actor = fetch_actor(actor)
if request.path in ['/inbox', '/actor'] and request.method.lower() == 'post':
if not request.actor:
return response.text('Could not get actor', status=400)
return response.json({'error': 'Could not get actor'}, status=400)
try:
data = request.data.json
except:
logging.verbose('Failed to parse post data')
return response.text(f'Invalid data', status=400)
return response.json({'error': f'Invalid data'}, status=400)
if type(request.actor).__name__ == 'Row':
logging.warning('Actor data is a db row:', actor)
return response.text(f'An unknown error happened', status=500)
logging.warning('Actor data is a db row:', request.actor)
return response.error({'error': f'An unknown error happened'}, status=500)
if not request.instance and data.get('type', '').lower() != 'follow':
return response.text(f'Follow the relay first', status=401)
return response.text({'error': f'Follow the relay first'}, status=401)
validated = verify_request(request, request.actor)
if not validated:
logging.debug(f'Not validated: {actor}')
return response.text(f'Failed signature check', status=401)
logging.debug(f'Not validated: {request.signature.actor}')
return response.text({'error': f'Failed signature check'}, status=401)
if any(map(request.path.startswith, auth_paths)) and not request.user:
return response.redir('/login')