mirror of
https://git.pleroma.social/pleroma/relay.git
synced 2024-11-10 02:17:59 +00:00
Compare commits
1 commit
7f403d232f
...
460237c1d5
Author | SHA1 | Date | |
---|---|---|---|
460237c1d5 |
|
@ -177,7 +177,7 @@ class Connection(tinysql.ConnectionMixin):
|
||||||
|
|
||||||
|
|
||||||
def get_hostnames(self):
|
def get_hostnames(self):
|
||||||
return tuple(row.domain for row in self.get_instances())
|
return tuple(row.domain for row in self.select('instances'))
|
||||||
|
|
||||||
|
|
||||||
def get_instance(self, data):
|
def get_instance(self, data):
|
||||||
|
@ -185,8 +185,7 @@ class Connection(tinysql.ConnectionMixin):
|
||||||
data = data.split('#', 1)[0]
|
data = data.split('#', 1)[0]
|
||||||
|
|
||||||
query = 'SELECT * FROM instances WHERE domain = :data OR actor = :data OR inbox = :data'
|
query = 'SELECT * FROM instances WHERE domain = :data OR actor = :data OR inbox = :data'
|
||||||
row = self.execute(query, dict(data=data), table='instances').one()
|
return self.execute(query, dict(data=data), table='instances').one()
|
||||||
return row if row.joined else None
|
|
||||||
|
|
||||||
|
|
||||||
def get_instances(self):
|
def get_instances(self):
|
||||||
|
@ -195,16 +194,11 @@ class Connection(tinysql.ConnectionMixin):
|
||||||
|
|
||||||
|
|
||||||
def get_request(self, domain):
|
def get_request(self, domain):
|
||||||
for instance in self.get_requests():
|
return self.select('instances', domain=domain, joined=None).one()
|
||||||
if instance.domain == domain:
|
|
||||||
return instance
|
|
||||||
|
|
||||||
raise KeyError(domain)
|
|
||||||
|
|
||||||
|
|
||||||
def get_requests(self):
|
def get_requests(self):
|
||||||
query = 'SELECT * FROM instances WHERE joined IS NULL'
|
self.select('instances', joined=None).all()
|
||||||
return self.execute(query, table='instances').all()
|
|
||||||
|
|
||||||
|
|
||||||
def get_whitelist(self):
|
def get_whitelist(self):
|
||||||
|
|
|
@ -212,8 +212,6 @@ class HttpClient(AppBase):
|
||||||
return await self.get(nodeinfo_url, loads=Nodeinfo.new_from_json) or False
|
return await self.get(nodeinfo_url, loads=Nodeinfo.new_from_json) or False
|
||||||
|
|
||||||
|
|
||||||
## http client methods can't be called directly from manage.py,
|
|
||||||
## so here's some wrapper functions
|
|
||||||
async def get(*args, **kwargs):
|
async def get(*args, **kwargs):
|
||||||
async with HttpClient() as client:
|
async with HttpClient() as client:
|
||||||
return await client.get(*args, **kwargs)
|
return await client.get(*args, **kwargs)
|
||||||
|
|
|
@ -6,7 +6,6 @@ import logging
|
||||||
import platform
|
import platform
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from datetime import datetime
|
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from . import __version__
|
from . import __version__
|
||||||
|
@ -385,77 +384,6 @@ def cli_inbox_remove(domain):
|
||||||
return click.echo(f'Error: Inbox does not exist: {domain}')
|
return click.echo(f'Error: Inbox does not exist: {domain}')
|
||||||
|
|
||||||
|
|
||||||
@cli.group('request')
|
|
||||||
def cli_request():
|
|
||||||
'Manage follow requests'
|
|
||||||
|
|
||||||
|
|
||||||
@cli_request.command('list')
|
|
||||||
def cli_request_list():
|
|
||||||
'List all the current follow requests'
|
|
||||||
|
|
||||||
click.echo('Follow requests:')
|
|
||||||
|
|
||||||
with app.database.session as s:
|
|
||||||
for row in s.get_requests():
|
|
||||||
click.echo(f'- {row.domain}')
|
|
||||||
|
|
||||||
|
|
||||||
@cli_request.command('approve')
|
|
||||||
@click.argument('domain')
|
|
||||||
def cli_request_approve(domain):
|
|
||||||
'Approve a follow request'
|
|
||||||
|
|
||||||
with app.database.session as s:
|
|
||||||
try:
|
|
||||||
instance = s.get_request(domain)
|
|
||||||
|
|
||||||
except KeyError:
|
|
||||||
return click.echo(f'No request for domain exists: {domain}')
|
|
||||||
|
|
||||||
data = {'joined': datetime.now()}
|
|
||||||
s.update('instances', data, id=instance.id)
|
|
||||||
|
|
||||||
asyncio.run(post(
|
|
||||||
instance.inbox,
|
|
||||||
Message.new_response(
|
|
||||||
host = app.config.host,
|
|
||||||
actor = instance.actor,
|
|
||||||
followid = instance.followid,
|
|
||||||
accept = True
|
|
||||||
)
|
|
||||||
))
|
|
||||||
|
|
||||||
return click.echo(f'Accepted follow request for domain: {domain}')
|
|
||||||
|
|
||||||
|
|
||||||
@cli_request.command('deny')
|
|
||||||
@click.argument('domain')
|
|
||||||
def cli_request_deny(domain):
|
|
||||||
'Deny a follow request'
|
|
||||||
|
|
||||||
with app.database.session as s:
|
|
||||||
try:
|
|
||||||
instance = s.get_request(domain)
|
|
||||||
|
|
||||||
except KeyError:
|
|
||||||
return click.echo(f'No request for domain exists: {domain}')
|
|
||||||
|
|
||||||
s.delete_instance(domain)
|
|
||||||
|
|
||||||
asyncio.run(post(
|
|
||||||
instance.inbox,
|
|
||||||
Message.new_response(
|
|
||||||
host = app.config.host,
|
|
||||||
actor = instance.actor,
|
|
||||||
followid = instance.followid,
|
|
||||||
accept = False
|
|
||||||
)
|
|
||||||
))
|
|
||||||
|
|
||||||
return click.echo(f'Denied follow request for domain: {domain}')
|
|
||||||
|
|
||||||
|
|
||||||
@cli.group('instance')
|
@cli.group('instance')
|
||||||
def cli_instance():
|
def cli_instance():
|
||||||
'Manage instance bans'
|
'Manage instance bans'
|
||||||
|
|
|
@ -192,15 +192,14 @@ class DotDict(dict):
|
||||||
|
|
||||||
class Message(DotDict):
|
class Message(DotDict):
|
||||||
@classmethod
|
@classmethod
|
||||||
def new_actor(cls, host, pubkey, name=None, description=None, locked=False):
|
def new_actor(cls, host, pubkey, description=None):
|
||||||
return cls({
|
return cls({
|
||||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
'id': f'https://{host}/actor',
|
'id': f'https://{host}/actor',
|
||||||
'type': 'Application',
|
'type': 'Application',
|
||||||
'preferredUsername': 'relay',
|
'preferredUsername': 'relay',
|
||||||
'name': name or 'ActivityRelay',
|
'name': 'ActivityRelay',
|
||||||
'summary': description or 'ActivityRelay bot',
|
'summary': description or 'ActivityRelay bot',
|
||||||
'manuallyApprovesFollowers': locked,
|
|
||||||
'followers': f'https://{host}/followers',
|
'followers': f'https://{host}/followers',
|
||||||
'following': f'https://{host}/following',
|
'following': f'https://{host}/following',
|
||||||
'inbox': f'https://{host}/inbox',
|
'inbox': f'https://{host}/inbox',
|
||||||
|
|
|
@ -26,11 +26,6 @@ async def handle_relay(request, s):
|
||||||
logging.verbose(f'already relayed {request.message.objectid}')
|
logging.verbose(f'already relayed {request.message.objectid}')
|
||||||
return
|
return
|
||||||
|
|
||||||
if request.message.get('to') != ['https://www.w3.org/ns/activitystreams#Public']:
|
|
||||||
logging.verbose('Message was not public')
|
|
||||||
logging.verbose(request.message.get('to'))
|
|
||||||
return
|
|
||||||
|
|
||||||
message = Message.new_announce(
|
message = Message.new_announce(
|
||||||
host = request.config.host,
|
host = request.config.host,
|
||||||
object = request.message.objectid
|
object = request.message.objectid
|
||||||
|
@ -73,17 +68,17 @@ async def handle_follow(request, s):
|
||||||
## reject if the actor isn't whitelisted while the whiltelist is enabled
|
## reject if the actor isn't whitelisted while the whiltelist is enabled
|
||||||
if s.get_config('whitelist') and not s.get_whitelist(request.actor.domain):
|
if s.get_config('whitelist') and not s.get_whitelist(request.actor.domain):
|
||||||
logging.verbose(f'Rejected actor for not being in the whitelist: {request.actor.id}')
|
logging.verbose(f'Rejected actor for not being in the whitelist: {request.actor.id}')
|
||||||
approve = False
|
accept = False
|
||||||
|
|
||||||
## reject if software used by actor is banned
|
## reject if software used by actor is banned
|
||||||
if s.get_ban('software', software):
|
if s.get_banned_software(software):
|
||||||
logging.verbose(f'Rejected follow from actor for using specific software: actor={request.actor.id}, software={software}')
|
logging.verbose(f'Rejected follow from actor for using specific software: actor={request.actor.id}, software={software}')
|
||||||
approve = False
|
accept = False
|
||||||
|
|
||||||
## reject if the actor is not an instance actor
|
## reject if the actor is not an instance actor
|
||||||
if person_check(request.actor, software):
|
if person_check(request.actor, software):
|
||||||
logging.verbose(f'Non-application actor tried to follow: {request.actor.id}')
|
logging.verbose(f'Non-application actor tried to follow: {request.actor.id}')
|
||||||
approve = False
|
accept = False
|
||||||
|
|
||||||
if approve:
|
if approve:
|
||||||
if not request.instance:
|
if not request.instance:
|
||||||
|
@ -94,7 +89,7 @@ async def handle_follow(request, s):
|
||||||
actor_data = request.actor,
|
actor_data = request.actor,
|
||||||
software = software,
|
software = software,
|
||||||
followid = request.message.id,
|
followid = request.message.id,
|
||||||
accept = not s.get_config('require_approval')
|
accept = s.get_config('require_approval')
|
||||||
)
|
)
|
||||||
|
|
||||||
if s.get_config('require_approval'):
|
if s.get_config('require_approval'):
|
||||||
|
@ -106,12 +101,11 @@ async def handle_follow(request, s):
|
||||||
followid = request.message.id
|
followid = request.message.id
|
||||||
)
|
)
|
||||||
|
|
||||||
# Rejects don't seem to work right with mastodon
|
|
||||||
request.app.push_message(
|
request.app.push_message(
|
||||||
request.actor.inbox,
|
request.actor.shared_inbox,
|
||||||
Message.new_response(
|
Message.new_response(
|
||||||
host = request.config.host,
|
host = request.config.host,
|
||||||
actor = request.message.actorid,
|
actor = request.actor.id,
|
||||||
followid = request.message.id,
|
followid = request.message.id,
|
||||||
accept = approve
|
accept = approve
|
||||||
)
|
)
|
||||||
|
@ -142,19 +136,8 @@ async def handle_undo(request, s):
|
||||||
if request.message.object.type != 'Follow':
|
if request.message.object.type != 'Follow':
|
||||||
return await handle_forward(request)
|
return await handle_forward(request)
|
||||||
|
|
||||||
instance_follow = request.instance.followid
|
|
||||||
message_follow = request.message.object.id
|
|
||||||
|
|
||||||
if person_check(request.actor, request.instance.software):
|
|
||||||
return logging.verbose(f'Non-application actor tried to unfollow: {request.actor.id}')
|
|
||||||
|
|
||||||
if instance_follow and instance_follow != message_follow:
|
|
||||||
return logging.verbose(f'Followid does not match: {instance_follow}, {message_follow}')
|
|
||||||
|
|
||||||
s.delete('instances', id=request.instance.id)
|
s.delete('instances', id=request.instance.id)
|
||||||
logging.verbose(f'Removed inbox: {request.instance.inbox}')
|
|
||||||
|
|
||||||
if request.instance.software != 'mastodon':
|
|
||||||
request.app.push_message(
|
request.app.push_message(
|
||||||
request.actor.shared_inbox,
|
request.actor.shared_inbox,
|
||||||
Message.new_unfollow(
|
Message.new_unfollow(
|
||||||
|
@ -180,18 +163,15 @@ async def run_processor(request):
|
||||||
return
|
return
|
||||||
|
|
||||||
with request.database.session as s:
|
with request.database.session as s:
|
||||||
if request.instance:
|
|
||||||
new_data = {}
|
new_data = {}
|
||||||
|
|
||||||
if not request.instance.software:
|
if request.instance and not request.instance.software:
|
||||||
logging.verbose(f'Fetching nodeinfo for instance: {request.instance.domain}')
|
|
||||||
nodeinfo = await request.app.client.fetch_nodeinfo(request.instance.domain)
|
nodeinfo = await request.app.client.fetch_nodeinfo(request.instance.domain)
|
||||||
|
|
||||||
if nodeinfo:
|
if nodeinfo:
|
||||||
new_data['software'] = nodeinfo.sw_name
|
new_data['software'] = nodeinfo.sw_name
|
||||||
|
|
||||||
if not request.instance.actor:
|
if not request.instance.actor:
|
||||||
logging.verbose(f'Fetching actor for instance: {request.instance.domain}')
|
|
||||||
new_data['actor'] = request.signature.keyid.split('#', 1)[0]
|
new_data['actor'] = request.signature.keyid.split('#', 1)[0]
|
||||||
|
|
||||||
if not request.instance.actor_data:
|
if not request.instance.actor_data:
|
||||||
|
|
|
@ -68,13 +68,10 @@ a:hover {{ color: #8AF; }}
|
||||||
|
|
||||||
|
|
||||||
@register_route('GET', '/actor', '/inbox')
|
@register_route('GET', '/actor', '/inbox')
|
||||||
async def actor(request, s):
|
async def actor(request):
|
||||||
data = Message.new_actor(
|
data = Message.new_actor(
|
||||||
host = request.config.host,
|
host = request.config.host,
|
||||||
pubkey = request.app.signer.pubkey,
|
pubkey = request.app.signer.pubkey
|
||||||
name = s.get_config('name'),
|
|
||||||
description = s.get_config('description'),
|
|
||||||
locked = s.get_config('require_approval')
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return Response.new(data, ctype='activity')
|
return Response.new(data, ctype='activity')
|
||||||
|
@ -138,7 +135,7 @@ async def inbox(request, s):
|
||||||
return Response.new_error(401, str(e), 'json')
|
return Response.new_error(401, str(e), 'json')
|
||||||
|
|
||||||
## reject if activity type isn't 'Follow' and the actor isn't following
|
## reject if activity type isn't 'Follow' and the actor isn't following
|
||||||
if request.message.type != 'Follow' and (not request.instance or not request.instance.joined):
|
if request.message.type != 'Follow' and not request.instance:
|
||||||
logging.verbose(f'Rejected actor for trying to post while not following: {request.actor.id}')
|
logging.verbose(f'Rejected actor for trying to post while not following: {request.actor.id}')
|
||||||
return Response.new_error(401, 'access denied', 'json')
|
return Response.new_error(401, 'access denied', 'json')
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue