Compare commits

..

No commits in common. "f7e1c6b0b88f6a50cde996f0b18d195cecc39155" and "1a7abb4ecb76bfc1292ca2bc46b37876b89186c6" have entirely different histories.

6 changed files with 84 additions and 35 deletions

View file

@ -234,8 +234,7 @@ class RelayConfig(DotDict):
def save(self): def save(self):
config = { config = {
# just turning config.db into a string is good enough for now 'db': self['db'],
'db': str(self.db),
'listen': self.listen, 'listen': self.listen,
'port': self.port, 'port': self.port,
'note': self.note, 'note': self.note,

View file

@ -1,9 +1,9 @@
import aputils
import logging import logging
import traceback import traceback
from aiohttp import ClientSession, ClientTimeout, TCPConnector from aiohttp import ClientSession, ClientTimeout, TCPConnector
from aiohttp.client_exceptions import ClientConnectorError, ServerTimeoutError from aiohttp.client_exceptions import ClientConnectorError, ServerTimeoutError
from aputils import Nodeinfo, WellKnownNodeinfo
from datetime import datetime from datetime import datetime
from cachetools import LRUCache from cachetools import LRUCache
from json.decoder import JSONDecodeError from json.decoder import JSONDecodeError
@ -87,7 +87,7 @@ class HttpClient:
headers = {} headers = {}
if sign_headers: if sign_headers:
headers.update(self.database.signer.sign_headers('GET', url, algorithm='original')) headers.update(self.database.signer.sign_headers('GET', url))
try: try:
logging.verbose(f'Fetching resource: {url}') logging.verbose(f'Fetching resource: {url}')
@ -103,7 +103,11 @@ class HttpClient:
return return
if loads: if loads:
message = await resp.json(loads=loads) if issubclass(loads, DotDict):
message = await resp.json(loads=loads.new_from_json)
else:
message = await resp.json(loads=loads)
elif resp.content_type == MIMETYPES['activity']: elif resp.content_type == MIMETYPES['activity']:
message = await resp.json(loads=Message.new_from_json) message = await resp.json(loads=Message.new_from_json)
@ -138,11 +142,11 @@ class HttpClient:
instance = self.database.get_inbox(url) instance = self.database.get_inbox(url)
## Using the old algo by default is probably a better idea right now ## Using the old algo by default is probably a better idea right now
if instance and instance.get('software') in {'mastodon'}: if instance and instance.get('software') not in {'mastodon'}:
algorithm = 'hs2019' algorithm = aputils.Algorithm.RSASHA256
else: else:
algorithm = 'original' algorithm = aputils.Algorithm.HS2019
headers = {'Content-Type': 'application/activity+json'} headers = {'Content-Type': 'application/activity+json'}
headers.update(self.database.signer.sign_headers('POST', url, message, algorithm=algorithm)) headers.update(self.database.signer.sign_headers('POST', url, message, algorithm=algorithm))
@ -169,10 +173,7 @@ class HttpClient:
## Additional methods ## ## Additional methods ##
async def fetch_nodeinfo(self, domain): async def fetch_nodeinfo(self, domain):
nodeinfo_url = None nodeinfo_url = None
wk_nodeinfo = await self.get( wk_nodeinfo = await self.get(f'https://{domain}/.well-known/nodeinfo', loads=WKNodeinfo)
f'https://{domain}/.well-known/nodeinfo',
loads = WellKnownNodeinfo.new_from_json
)
for version in ['20', '21']: for version in ['20', '21']:
try: try:
@ -185,4 +186,4 @@ class HttpClient:
logging.verbose(f'Failed to fetch nodeinfo url for domain: {domain}') logging.verbose(f'Failed to fetch nodeinfo url for domain: {domain}')
return False return False
return await self.get(nodeinfo_url, loads=Nodeinfo.new_from_json) or False return await request(nodeinfo_url, loads=Nodeinfo) or False

View file

@ -37,6 +37,10 @@ def set_app(new_app):
app = new_app app = new_app
def build_signing_string(headers, used_headers):
return '\n'.join(map(lambda x: ': '.join([x.lower(), headers[x]]), used_headers))
def boolean(value): def boolean(value):
if isinstance(value, str): if isinstance(value, str):
if value.lower() in ['on', 'y', 'yes', 'true', 'enable', 'enabled', '1']: if value.lower() in ['on', 'y', 'yes', 'true', 'enable', 'enabled', '1']:
@ -275,6 +279,12 @@ class Message(DotDict):
return aputils.Signer.new_from_actor(self) return aputils.Signer.new_from_actor(self)
class Nodeinfo(DotDict):
@property
def swname(self):
return self.software.name
class Response(AiohttpResponse): class Response(AiohttpResponse):
@classmethod @classmethod
def new(cls, body='', status=200, headers=None, ctype='text'): def new(cls, body='', status=200, headers=None, ctype='text'):
@ -340,3 +350,22 @@ class View(AiohttpView):
@property @property
def database(self): def database(self):
return self.app.database return self.app.database
class WKNodeinfo(DotDict):
@classmethod
def new(cls, v20, v21):
return cls({
'links': [
{'rel': NODEINFO_NS['20'], 'href': v20},
{'rel': NODEINFO_NS['21'], 'href': v21}
]
})
def get_url(self, version='20'):
for item in self.links:
if item['rel'] == NODEINFO_NS[version]:
return item['href']
raise KeyError(version)

View file

@ -23,7 +23,7 @@ async def handle_relay(request):
cache[request.message.objectid] = message.id cache[request.message.objectid] = message.id
logging.debug(f'>> relay: {message}') logging.debug(f'>> relay: {message}')
inboxes = request.database.distill_inboxes(request.message) niboxes = request.database.distill_inboxes(request.message)
for inbox in inboxes: for inbox in inboxes:
request.app.push_message(inbox, message) request.app.push_message(inbox, message)

View file

@ -8,7 +8,7 @@ from pathlib import Path
from . import __version__, misc from . import __version__, misc
from .http_debug import STATS from .http_debug import STATS
from .misc import DotDict, Message, Response from .misc import DotDict, Message, Response, WKNodeinfo
from .processors import run_processor from .processors import run_processor
@ -158,37 +158,57 @@ async def webfinger(request):
if subject != f'acct:relay@{request.config.host}': if subject != f'acct:relay@{request.config.host}':
return Response.new_error(404, 'user not found', 'json') return Response.new_error(404, 'user not found', 'json')
data = aputils.Webfinger.new( data = {
handle = 'relay', 'subject': subject,
domain = request.config.host, 'aliases': [request.config.actor],
actor = request.config.actor 'links': [
) {'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\"'}
]
}
return Response.new(data, ctype='json') return Response.new(data, ctype='json')
@register_route('GET', '/nodeinfo/{version:\d.\d\.json}') @register_route('GET', '/nodeinfo/{version:\d.\d\.json}')
async def nodeinfo(request): async def nodeinfo_2_0(request):
niversion = request.match_info['version'][:3] niversion = request.match_info['version'][:3]
data = {
'openRegistrations': not request.config.whitelist_enabled,
'protocols': ['activitypub'],
'services': {
'inbound': [],
'outbound': []
},
'software': {
'name': 'activityrelay',
'version': version
},
'usage': {
'localPosts': 0,
'users': {
'total': 1
}
},
'metadata': {
'peers': request.database.hostnames
},
'version': niversion
}
data = dict( if version == '2.1':
name = 'activityrelay', data['software']['repository'] = 'https://git.pleroma.social/pleroma/relay'
version = version,
protocols = ['activitypub'],
open_regs = not request.config.whitelist_enabled,
users = 1,
metadata = {'peers': request.database.hostnames}
)
if niversion == '2.1': return Response.new(data, ctype='json')
data['repo'] = 'https://git.pleroma.social/pleroma/relay'
return Response.new(aputils.Nodeinfo.new(**data), ctype='json')
@register_route('GET', '/.well-known/nodeinfo') @register_route('GET', '/.well-known/nodeinfo')
async def nodeinfo_wellknown(request): async def nodeinfo_wellknown(request):
data = aputils.WellKnownNodeinfo.new_template(request.config.host) data = WKNodeinfo.new(
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') return Response.new(data, ctype='json')

View file

@ -28,7 +28,7 @@ install_requires =
click >= 8.1.2 click >= 8.1.2
pycryptodome >= 3.14.1 pycryptodome >= 3.14.1
PyYAML >= 5.0.0 PyYAML >= 5.0.0
aputils @ https://git.barkshark.xyz/barkshark/aputils/archive/0.1.2.tar.gz aputils @ https://git.barkshark.xyz/barkshark/aputils/archive/0.1.1.tar.gz
python_requires = >=3.6 python_requires = >=3.6
[options.extras_require] [options.extras_require]