mirror of
https://git.pleroma.social/pleroma/relay.git
synced 2024-12-22 19:21:08 +00:00
create Response class
This commit is contained in:
parent
6af9c8e6fe
commit
ff95a3033d
|
@ -10,6 +10,7 @@ from Crypto.Hash import SHA, SHA256, SHA512
|
|||
from Crypto.PublicKey import RSA
|
||||
from Crypto.Signature import PKCS1_v1_5
|
||||
from aiohttp import ClientSession
|
||||
from aiohttp.web import Response as AiohttpResponse
|
||||
from datetime import datetime
|
||||
from json.decoder import JSONDecodeError
|
||||
from urllib.parse import urlparse
|
||||
|
@ -26,6 +27,13 @@ HASHES = {
|
|||
'sha512': SHA512
|
||||
}
|
||||
|
||||
MIMETYPES = {
|
||||
'activity': 'application/activity+json',
|
||||
'html': 'text/html',
|
||||
'json': 'application/json',
|
||||
'plain': 'text/plain'
|
||||
}
|
||||
|
||||
NODEINFO_NS = {
|
||||
'20': 'http://nodeinfo.diaspora.software/ns/schema/2.0',
|
||||
'21': 'http://nodeinfo.diaspora.software/ns/schema/2.1'
|
||||
|
@ -166,12 +174,12 @@ async def request(uri, data=None, force=False, sign_headers=True, activity=True)
|
|||
method = 'POST' if data else 'GET'
|
||||
action = data.get('type') if data else None
|
||||
headers = {
|
||||
'Accept': 'application/activity+json, application/json;q=0.9',
|
||||
'Accept': f'{MIMETYPES["activity"]}, {MIMETYPES["json"]};q=0.9',
|
||||
'User-Agent': 'ActivityRelay',
|
||||
}
|
||||
|
||||
if data:
|
||||
headers['Content-Type'] = 'application/activity+json' if activity else 'application/json'
|
||||
headers['Content-Type'] = MIMETYPES['activity' if activity else 'json']
|
||||
|
||||
if sign_headers:
|
||||
signing_headers = {
|
||||
|
@ -219,10 +227,10 @@ async def request(uri, data=None, force=False, sign_headers=True, activity=True)
|
|||
|
||||
return logging.verbose(f'Received error when sending {action} to {uri}: {resp.status} {resp_data}')
|
||||
|
||||
if resp.content_type == 'application/activity+json':
|
||||
if resp.content_type == MIMETYPES['activity']:
|
||||
resp_data = await resp.json(loads=Message.new_from_json)
|
||||
|
||||
elif resp.content_type == 'application/json':
|
||||
elif resp.content_type == MIMETYPES['json']:
|
||||
resp_data = await resp.json(loads=DotDict.new_from_json)
|
||||
|
||||
else:
|
||||
|
@ -453,6 +461,45 @@ class Message(DotDict):
|
|||
return self.object
|
||||
|
||||
|
||||
class Response(AiohttpResponse):
|
||||
@classmethod
|
||||
def new(cls, body='', status=200, headers=None, ctype='text'):
|
||||
kwargs = {
|
||||
'status': status,
|
||||
'headers': headers,
|
||||
'content_type': MIMETYPES[ctype]
|
||||
}
|
||||
|
||||
if isinstance(body, bytes):
|
||||
kwargs['body'] = body
|
||||
|
||||
elif isinstance(body, dict) and ctype in {'json', 'activity'}:
|
||||
kwargs['text'] = json.dumps(body)
|
||||
|
||||
else:
|
||||
kwargs['text'] = body
|
||||
|
||||
return cls(**kwargs)
|
||||
|
||||
|
||||
@classmethod
|
||||
def new_error(cls, status, body, ctype='plain'):
|
||||
if ctype == 'json':
|
||||
body = json.dumps({'status': status, 'error': body})
|
||||
|
||||
return cls(body=body, status=status, ctype=ctype)
|
||||
|
||||
|
||||
@property
|
||||
def location(self):
|
||||
return self.headers.get('Location')
|
||||
|
||||
|
||||
@location.setter
|
||||
def location(self, value):
|
||||
self.headers['Location'] = value
|
||||
|
||||
|
||||
class WKNodeinfo(DotDict):
|
||||
@classmethod
|
||||
def new(cls, v20, v21):
|
||||
|
|
|
@ -2,16 +2,21 @@ import logging
|
|||
import subprocess
|
||||
import traceback
|
||||
|
||||
from aiohttp.web import HTTPForbidden, HTTPUnauthorized, Response, json_response, route
|
||||
|
||||
from . import __version__, misc
|
||||
from .http_debug import STATS
|
||||
from .misc import Message, WKNodeinfo
|
||||
from .misc import Message, Response, WKNodeinfo
|
||||
from .processors import run_processor
|
||||
|
||||
|
||||
routes = []
|
||||
|
||||
try:
|
||||
commit_label = subprocess.check_output(["git", "rev-parse", "HEAD"]).strip().decode('ascii')
|
||||
version = f'{__version__} {commit_label}'
|
||||
|
||||
except:
|
||||
version = __version__
|
||||
|
||||
|
||||
def register_route(method, path):
|
||||
def wrapper(func):
|
||||
|
@ -21,14 +26,6 @@ def register_route(method, path):
|
|||
return wrapper
|
||||
|
||||
|
||||
try:
|
||||
commit_label = subprocess.check_output(["git", "rev-parse", "HEAD"]).strip().decode('ascii')
|
||||
version = f'{__version__} {commit_label}'
|
||||
|
||||
except:
|
||||
version = __version__
|
||||
|
||||
|
||||
@register_route('GET', '/')
|
||||
async def home(request):
|
||||
targets = '<br>'.join(request.app.database.hostnames)
|
||||
|
@ -55,12 +52,7 @@ a:hover {{ color: #8AF; }}
|
|||
<br><p>List of {count} registered instances:<br>{targets}</p>
|
||||
</body></html>"""
|
||||
|
||||
return Response(
|
||||
status = 200,
|
||||
content_type = 'text/html',
|
||||
charset = 'utf-8',
|
||||
text = text
|
||||
)
|
||||
return Response.new(text, ctype='html')
|
||||
|
||||
|
||||
@register_route('GET', '/inbox')
|
||||
|
@ -71,7 +63,7 @@ async def actor(request):
|
|||
pubkey = request.app.database.pubkey
|
||||
)
|
||||
|
||||
return json_response(data, content_type='application/activity+json')
|
||||
return Response.new(data, ctype='activity')
|
||||
|
||||
|
||||
@register_route('POST', '/inbox')
|
||||
|
@ -95,29 +87,29 @@ async def inbox(request):
|
|||
## reject if there is no actor in the message
|
||||
except KeyError:
|
||||
logging.verbose('actor not in data')
|
||||
raise HTTPUnauthorized(body='no actor in message')
|
||||
return Response.new_error(400, 'no actor in message', 'json')
|
||||
|
||||
except:
|
||||
traceback.print_exc()
|
||||
logging.verbose('Failed to parse inbox message')
|
||||
raise HTTPUnauthorized(body='failed to parse message')
|
||||
return Response.new_error(400, 'failed to parse message', 'json')
|
||||
|
||||
actor = await misc.request(data.actorid)
|
||||
|
||||
## reject if actor is empty
|
||||
if not actor:
|
||||
logging.verbose(f'Failed to fetch actor: {data.actorid}')
|
||||
raise HTTPUnauthorized('failed to fetch actor')
|
||||
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(data.domain):
|
||||
logging.verbose(f'Rejected actor for not being in the whitelist: {data.actorid}')
|
||||
raise HTTPForbidden(body='access denied')
|
||||
return Response.new_error(403, 'access denied', 'json')
|
||||
|
||||
## reject if actor is banned
|
||||
if request.app['config'].is_banned(data.domain):
|
||||
logging.verbose(f'Ignored request from banned actor: {data.actorid}')
|
||||
raise HTTPForbidden(body='access denied')
|
||||
return Response.new_error(403, 'access denied', 'json')
|
||||
|
||||
## reject if software used by actor is banned
|
||||
if len(config.blocked_software):
|
||||
|
@ -125,22 +117,22 @@ async def inbox(request):
|
|||
|
||||
if config.is_banned_software(software):
|
||||
logging.verbose(f'Rejected actor for using specific software: {software}')
|
||||
raise HTTPForbidden(body='access denied')
|
||||
return Response.new_error(403, 'access denied', 'json')
|
||||
|
||||
## reject if the signature is invalid
|
||||
if not (await misc.validate_signature(data.actorid, request)):
|
||||
logging.verbose(f'signature validation failed for: {data.actorid}')
|
||||
raise HTTPUnauthorized(body='signature check failed, signature did not match key')
|
||||
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(data.domain):
|
||||
logging.verbose(f'Rejected actor for trying to post while not following: {data.actorid}')
|
||||
raise HTTPUnauthorized(body='access denied')
|
||||
return Response.new_error(401, 'access denied', 'json')
|
||||
|
||||
logging.debug(f">> payload {data}")
|
||||
|
||||
await run_processor(request, actor, data, software)
|
||||
return Response(body=b'{}', content_type='application/activity+json')
|
||||
return Response.new(status=202)
|
||||
|
||||
|
||||
@register_route('GET', '/.well-known/webfinger')
|
||||
|
@ -148,7 +140,7 @@ async def webfinger(request):
|
|||
subject = request.query['resource']
|
||||
|
||||
if subject != f'acct:relay@{request.app.config.host}':
|
||||
return json_response({'error': 'user not found'}, status=404)
|
||||
return Response.new_error(404, 'user not found', 'json')
|
||||
|
||||
data = {
|
||||
'subject': subject,
|
||||
|
@ -159,7 +151,7 @@ async def webfinger(request):
|
|||
]
|
||||
}
|
||||
|
||||
return json_response(data)
|
||||
return Response.new(data, ctype='json')
|
||||
|
||||
|
||||
@register_route('GET', '/nodeinfo/{version:\d.\d\.json}')
|
||||
|
@ -191,7 +183,7 @@ async def nodeinfo_2_0(request):
|
|||
if version == '2.1':
|
||||
data['software']['repository'] = 'https://git.pleroma.social/pleroma/relay'
|
||||
|
||||
return json_response(data)
|
||||
return Response.new(data, ctype='json')
|
||||
|
||||
|
||||
@register_route('GET', '/.well-known/nodeinfo')
|
||||
|
@ -201,9 +193,9 @@ async def nodeinfo_wellknown(request):
|
|||
v21 = f'https://{request.app.config.host}/nodeinfo/2.1.json'
|
||||
)
|
||||
|
||||
return json_response(data)
|
||||
return Response.new(data, ctype='json')
|
||||
|
||||
|
||||
@register_route('GET', '/stats')
|
||||
async def stats(request):
|
||||
return json_response(STATS)
|
||||
return Response.new(STATS, ctype='json')
|
||||
|
|
Loading…
Reference in a new issue