use new request properties and only fetch nodeinfo on follow
This commit is contained in:
parent
4a8a8da740
commit
ba9f2718aa
|
@ -6,92 +6,97 @@ from uuid import uuid4
|
|||
from . import misc
|
||||
|
||||
|
||||
async def handle_relay(request, actor, data, nodeinfo):
|
||||
if data.objectid in request.app.cache.objects:
|
||||
logging.verbose(f'already relayed {data.objectid}')
|
||||
async def handle_relay(request):
|
||||
if request.message.objectid in request.cache.objects:
|
||||
logging.verbose(f'already relayed {request.message.objectid}')
|
||||
return
|
||||
|
||||
logging.verbose(f'Relaying post from {data.actorid}')
|
||||
logging.verbose(f'Relaying post from {request.message.actorid}')
|
||||
|
||||
message = misc.Message.new_announce(
|
||||
host = request.app.config.host,
|
||||
object = data.objectid
|
||||
host = request.config.host,
|
||||
object = request.message.objectid
|
||||
)
|
||||
|
||||
logging.debug(f'>> relay: {message}')
|
||||
|
||||
inboxes = misc.distill_inboxes(actor, data.objectid)
|
||||
inboxes = misc.distill_inboxes(request.actor, request.message.objectid)
|
||||
futures = [misc.request(inbox, data=message) for inbox in inboxes]
|
||||
|
||||
asyncio.ensure_future(asyncio.gather(*futures))
|
||||
request.app.cache.objects[data.objectid] = message.id
|
||||
request.cache.objects[request.message.objectid] = message.id
|
||||
|
||||
|
||||
async def handle_forward(request, actor, data, nodeinfo):
|
||||
if data.id in request.app.cache.objects:
|
||||
logging.verbose(f'already forwarded {data.id}')
|
||||
async def handle_forward(request):
|
||||
if request.message.id in request.cache.objects:
|
||||
logging.verbose(f'already forwarded {request.message.id}')
|
||||
return
|
||||
|
||||
message = misc.Message.new_announce(
|
||||
host = request.app.config.host,
|
||||
object = data
|
||||
host = request.config.host,
|
||||
object = request.message
|
||||
)
|
||||
|
||||
logging.verbose(f'Forwarding post from {actor.id}')
|
||||
logging.debug(f'>> Relay {data}')
|
||||
logging.verbose(f'Forwarding post from {request.actor.id}')
|
||||
logging.debug(f'>> Relay {request.message}')
|
||||
|
||||
inboxes = misc.distill_inboxes(actor, data.id)
|
||||
inboxes = misc.distill_inboxes(request.actor, request.message.id)
|
||||
futures = [misc.request(inbox, data=message) for inbox in inboxes]
|
||||
|
||||
asyncio.ensure_future(asyncio.gather(*futures))
|
||||
request.app.cache.objects[data.id] = message.id
|
||||
request.cache.objects[request.message.id] = message.id
|
||||
|
||||
|
||||
async def handle_follow(request, actor, data, nodeinfo):
|
||||
if not request.app.database.add_inbox(actor.shared_inbox, data.id):
|
||||
request.app.database.set_followid(actor.id, data.id)
|
||||
async def handle_follow(request):
|
||||
nodeinfo = await misc.fetch_nodeinfo(request.actor.domain)
|
||||
software = nodeinfo.swname if nodeinfo else None
|
||||
|
||||
request.app.database.save()
|
||||
## reject if software used by actor is banned
|
||||
if request.config.is_banned_software(software):
|
||||
return logging.verbose(f'Rejected follow from actor for using specific software: actor={request.actor.id}, software={software}')
|
||||
|
||||
request.database.add_inbox(request.actor.shared_inbox, request.message.id, software)
|
||||
request.database.save()
|
||||
|
||||
await misc.request(
|
||||
actor.shared_inbox,
|
||||
request.actor.shared_inbox,
|
||||
misc.Message.new_response(
|
||||
host = request.app.config.host,
|
||||
actor = actor.id,
|
||||
followid = data.id,
|
||||
host = request.config.host,
|
||||
actor = request.actor.id,
|
||||
followid = request.message.id,
|
||||
accept = True
|
||||
)
|
||||
)
|
||||
|
||||
# Are Akkoma and Pleroma the only two that expect a follow back?
|
||||
# Ignoring only Mastodon for now
|
||||
if nodeinfo.swname != 'mastodon':
|
||||
if software != 'mastodon':
|
||||
await misc.request(
|
||||
actor.shared_inbox,
|
||||
request.actor.shared_inbox,
|
||||
misc.Message.new_follow(
|
||||
host = request.app.config.host,
|
||||
actor = actor.id
|
||||
host = request.config.host,
|
||||
actor = request.actor.id
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def handle_undo(request, actor, data, nodeinfo):
|
||||
async def handle_undo(request):
|
||||
## If the object is not a Follow, forward it
|
||||
if data['object']['type'] != 'Follow':
|
||||
return await handle_forward(request, actor, data, nodeinfo)
|
||||
if request.message.object.type != 'Follow':
|
||||
return await handle_forward(request)
|
||||
|
||||
if not request.app.database.del_inbox(actor.domain, data.id):
|
||||
if not request.database.del_inbox(request.actor.domain, request.message.id):
|
||||
return
|
||||
|
||||
request.app.database.save()
|
||||
request.database.save()
|
||||
|
||||
message = misc.Message.new_unfollow(
|
||||
host = request.app.config.host,
|
||||
actor = actor.id,
|
||||
follow = data
|
||||
host = request.config.host,
|
||||
actor = request.actor.id,
|
||||
follow = request.message
|
||||
)
|
||||
|
||||
await misc.request(actor.shared_inbox, message)
|
||||
await misc.request(request.actor.shared_inbox, message)
|
||||
|
||||
|
||||
processors = {
|
||||
|
@ -104,9 +109,9 @@ processors = {
|
|||
}
|
||||
|
||||
|
||||
async def run_processor(request, actor, data, nodeinfo):
|
||||
if data.type not in processors:
|
||||
async def run_processor(request):
|
||||
if request.message.type not in processors:
|
||||
return
|
||||
|
||||
logging.verbose(f'New "{data.type}" from actor: {actor.id}')
|
||||
return await processors[data.type](request, actor, data, nodeinfo)
|
||||
logging.verbose(f'New "{request.message.type}" from actor: {request.actor.id}')
|
||||
return await processors[request.message.type](request)
|
||||
|
|
|
@ -33,10 +33,10 @@ def register_route(method, path):
|
|||
|
||||
@register_route('GET', '/')
|
||||
async def home(request):
|
||||
targets = '<br>'.join(request.app.database.hostnames)
|
||||
note = request.app.config.note
|
||||
count = len(request.app.database.hostnames)
|
||||
host = request.app.config.host
|
||||
targets = '<br>'.join(request.database.hostnames)
|
||||
note = request.config.note
|
||||
count = len(request.database.hostnames)
|
||||
host = request.config.host
|
||||
|
||||
text = f"""
|
||||
<html><head>
|
||||
|
@ -64,8 +64,8 @@ a:hover {{ color: #8AF; }}
|
|||
@register_route('GET', '/actor')
|
||||
async def actor(request):
|
||||
data = Message.new_actor(
|
||||
host = request.app.config.host,
|
||||
pubkey = request.app.database.pubkey
|
||||
host = request.config.host,
|
||||
pubkey = request.database.pubkey
|
||||
)
|
||||
|
||||
return Response.new(data, ctype='activity')
|
||||
|
@ -74,67 +74,63 @@ async def actor(request):
|
|||
@register_route('POST', '/inbox')
|
||||
@register_route('POST', '/actor')
|
||||
async def inbox(request):
|
||||
config = request.app.config
|
||||
database = request.app.database
|
||||
config = request.config
|
||||
database = request.database
|
||||
|
||||
## reject if missing signature header
|
||||
try:
|
||||
signature = DotDict.new_from_signature(request.headers['signature'])
|
||||
|
||||
except KeyError:
|
||||
if not request.signature:
|
||||
logging.verbose('Actor missing signature header')
|
||||
raise HTTPUnauthorized(body='missing signature')
|
||||
|
||||
## read message
|
||||
try:
|
||||
data = await request.json(loads=Message.new_from_json)
|
||||
request['message'] = await request.json(loads=Message.new_from_json)
|
||||
|
||||
## reject if there is no message
|
||||
if not request.message:
|
||||
logging.verbose('empty message')
|
||||
return Response.new_error(400, 'missing message', 'json')
|
||||
|
||||
## reject if there is no actor in the message
|
||||
if 'actor' not in data:
|
||||
logging.verbose('actor not in data')
|
||||
if 'actor' not in request.message:
|
||||
logging.verbose('actor not in message')
|
||||
return Response.new_error(400, 'no actor in message', 'json')
|
||||
|
||||
except:
|
||||
## this code should hopefully never get called
|
||||
traceback.print_exc()
|
||||
logging.verbose('Failed to parse inbox message')
|
||||
return Response.new_error(400, 'failed to parse message', 'json')
|
||||
|
||||
actor = await misc.request(signature.keyid)
|
||||
nodeinfo = await misc.fetch_nodeinfo(actor.domain)
|
||||
request['actor'] = await misc.request(request.signature.keyid)
|
||||
|
||||
## reject if actor is empty
|
||||
if not actor:
|
||||
logging.verbose(f'Failed to fetch actor: {actor.id}')
|
||||
if not request.actor:
|
||||
logging.verbose(f'Failed to fetch actor: {request.actor.id}')
|
||||
return Response.new_error(400, 'failed to fetch actor', 'json')
|
||||
|
||||
## reject if the actor isn't whitelisted while the whiltelist is enabled
|
||||
elif config.whitelist_enabled and not config.is_whitelisted(actor.domain):
|
||||
logging.verbose(f'Rejected actor for not being in the whitelist: {actor.id}')
|
||||
if config.whitelist_enabled and not config.is_whitelisted(request.actor.domain):
|
||||
logging.verbose(f'Rejected actor for not being in the whitelist: {request.actor.id}')
|
||||
return Response.new_error(403, 'access denied', 'json')
|
||||
|
||||
## reject if actor is banned
|
||||
if request.app['config'].is_banned(actor.domain):
|
||||
if request.config.is_banned(request.actor.domain):
|
||||
logging.verbose(f'Ignored request from banned actor: {actor.id}')
|
||||
return Response.new_error(403, 'access denied', 'json')
|
||||
|
||||
## reject if software used by actor is banned
|
||||
if config.is_banned_software(nodeinfo.swname):
|
||||
logging.verbose(f'Rejected actor for using specific software: {nodeinfo.swname}')
|
||||
return Response.new_error(403, 'access denied', 'json')
|
||||
|
||||
## reject if the signature is invalid
|
||||
if not (await misc.validate_signature(actor, signature, request)):
|
||||
if not (await misc.validate_signature(request.actor, request.signature, request)):
|
||||
logging.verbose(f'signature validation failed for: {actor.id}')
|
||||
return Response.new_error(401, 'signature check failed', 'json')
|
||||
|
||||
## reject if activity type isn't 'Follow' and the actor isn't following
|
||||
if data['type'] != 'Follow' and not database.get_inbox(actor.domain):
|
||||
logging.verbose(f'Rejected actor for trying to post while not following: {actor.id}')
|
||||
if request.message.type != 'Follow' and not database.get_inbox(request.actor.domain):
|
||||
logging.verbose(f'Rejected actor for trying to post while not following: {request.actor.id}')
|
||||
return Response.new_error(401, 'access denied', 'json')
|
||||
|
||||
logging.debug(f">> payload {data}")
|
||||
logging.debug(f">> payload {request.message.to_json(4)}")
|
||||
|
||||
await run_processor(request, actor, data, nodeinfo)
|
||||
await run_processor(request)
|
||||
return Response.new(status=202)
|
||||
|
||||
|
||||
|
@ -146,15 +142,15 @@ async def webfinger(request):
|
|||
except KeyError:
|
||||
return Response.new_error(400, 'missing \'resource\' query key', 'json')
|
||||
|
||||
if subject != f'acct:relay@{request.app.config.host}':
|
||||
if subject != f'acct:relay@{request.config.host}':
|
||||
return Response.new_error(404, 'user not found', 'json')
|
||||
|
||||
data = {
|
||||
'subject': subject,
|
||||
'aliases': [request.app.config.actor],
|
||||
'aliases': [request.config.actor],
|
||||
'links': [
|
||||
{'href': request.app.config.actor, 'rel': 'self', 'type': 'application/activity+json'},
|
||||
{'href': request.app.config.actor, 'rel': 'self', 'type': 'application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"'}
|
||||
{'href': request.config.actor, 'rel': 'self', 'type': 'application/activity+json'},
|
||||
{'href': request.config.actor, 'rel': 'self', 'type': 'application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"'}
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -165,7 +161,7 @@ async def webfinger(request):
|
|||
async def nodeinfo_2_0(request):
|
||||
niversion = request.match_info['version'][:3]
|
||||
data = {
|
||||
'openRegistrations': not request.app.config.whitelist_enabled,
|
||||
'openRegistrations': not request.config.whitelist_enabled,
|
||||
'protocols': ['activitypub'],
|
||||
'services': {
|
||||
'inbound': [],
|
||||
|
@ -182,7 +178,7 @@ async def nodeinfo_2_0(request):
|
|||
}
|
||||
},
|
||||
'metadata': {
|
||||
'peers': request.app.database.hostnames
|
||||
'peers': request.database.hostnames
|
||||
},
|
||||
'version': niversion
|
||||
}
|
||||
|
@ -196,8 +192,8 @@ async def nodeinfo_2_0(request):
|
|||
@register_route('GET', '/.well-known/nodeinfo')
|
||||
async def nodeinfo_wellknown(request):
|
||||
data = WKNodeinfo.new(
|
||||
v20 = f'https://{request.app.config.host}/nodeinfo/2.0.json',
|
||||
v21 = f'https://{request.app.config.host}/nodeinfo/2.1.json'
|
||||
v20 = f'https://{request.config.host}/nodeinfo/2.0.json',
|
||||
v21 = f'https://{request.config.host}/nodeinfo/2.1.json'
|
||||
)
|
||||
|
||||
return Response.new(data, ctype='json')
|
||||
|
|
Loading…
Reference in a new issue