Compare commits

..

No commits in common. "ae490a9bf3e4f971ec4d4bc1c6506791ff246b4c" and "6f03e2ad4cffd5ca91359785801acce24b652ff7" have entirely different histories.

7 changed files with 39 additions and 25 deletions

View file

@ -15,7 +15,7 @@ the [official pipx docs](https://pypa.github.io/pipx/installation/) for more in-
Now simply install ActivityRelay directly from git Now simply install ActivityRelay directly from git
pipx install git+https://git.pleroma.social/pleroma/relay@0.3.0 pipx install git+https://git.pleroma.social/pleroma/relay@0.2.5
Or from a cloned git repo. Or from a cloned git repo.
@ -39,7 +39,7 @@ be installed via [pyenv](https://github.com/pyenv/pyenv).
The instructions for installation via pip are very similar to pipx. Installation can be done from The instructions for installation via pip are very similar to pipx. Installation can be done from
git git
python3 -m pip install git+https://git.pleroma.social/pleroma/relay@0.3.0 python3 -m pip install git+https://git.pleroma.social/pleroma/relay@0.2.5
or a cloned git repo. or a cloned git repo.

View file

@ -1 +1 @@
__version__ = '0.3.0' __version__ = '0.2.6'

View file

@ -1,13 +1,13 @@
from __future__ import annotations from __future__ import annotations
import Crypto import Crypto
import aputils
import asyncio import asyncio
import click import click
import os import os
import platform import platform
import typing import typing
from aputils.signer import Signer
from pathlib import Path from pathlib import Path
from shutil import copyfile from shutil import copyfile
from urllib.parse import urlparse from urllib.parse import urlparse
@ -193,7 +193,7 @@ def cli_setup(ctx: click.Context) -> None:
ctx.obj.config.save() ctx.obj.config.save()
config = { config = {
'private-key': aputils.Signer.new('n/a').export() 'private-key': Signer.new('n/a').export()
} }
with ctx.obj.database.session() as conn: with ctx.obj.database.session() as conn:

View file

@ -1,12 +1,12 @@
from __future__ import annotations from __future__ import annotations
import aputils
import json import json
import os import os
import socket import socket
import typing import typing
from aiohttp.web import Response as AiohttpResponse from aiohttp.web import Response as AiohttpResponse
from aputils.message import Message as ApMessage
from datetime import datetime from datetime import datetime
from uuid import uuid4 from uuid import uuid4
@ -95,7 +95,7 @@ class JsonEncoder(json.JSONEncoder):
return json.JSONEncoder.default(self, o) return json.JSONEncoder.default(self, o)
class Message(aputils.Message): class Message(ApMessage):
@classmethod @classmethod
def new_actor(cls: type[Message], # pylint: disable=arguments-differ def new_actor(cls: type[Message], # pylint: disable=arguments-differ
host: str, host: str,
@ -182,6 +182,16 @@ class Message(aputils.Message):
}) })
# todo: remove when fixed in aputils
@property
def object_id(self) -> str:
try:
return self["object"]["id"]
except (KeyError, TypeError):
return self["object"]
class Response(AiohttpResponse): class Response(AiohttpResponse):
# AiohttpResponse.__len__ method returns 0, so bool(response) always returns False # AiohttpResponse.__len__ method returns 0, so bool(response) always returns False
def __bool__(self) -> bool: def __bool__(self) -> bool:

View file

@ -1,9 +1,12 @@
from __future__ import annotations from __future__ import annotations
import aputils
import traceback import traceback
import typing import typing
from aputils.errors import SignatureFailureError
from aputils.misc import Digest, HttpDate, Signature
from aputils.objects import Webfinger
from .base import View, register_route from .base import View, register_route
from .. import logger as logging from .. import logger as logging
@ -12,6 +15,7 @@ from ..processors import run_processor
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from aiohttp.web import Request from aiohttp.web import Request
from aputils.signer import Signer
from tinysql import Row from tinysql import Row
@ -22,11 +26,11 @@ class ActorView(View):
def __init__(self, request: Request): def __init__(self, request: Request):
View.__init__(self, request) View.__init__(self, request)
self.signature: aputils.Signature = None self.signature: Signature = None
self.message: Message = None self.message: Message = None
self.actor: Message = None self.actor: Message = None
self.instance: Row = None self.instance: Row = None
self.signer: aputils.Signer = None self.signer: Signer = None
async def get(self, request: Request) -> Response: async def get(self, request: Request) -> Response:
@ -73,7 +77,7 @@ class ActorView(View):
async def get_post_data(self) -> Response | None: async def get_post_data(self) -> Response | None:
try: try:
self.signature = aputils.Signature.new_from_signature(self.request.headers['signature']) self.signature = Signature.new_from_signature(self.request.headers['signature'])
except KeyError: except KeyError:
logging.verbose('Missing signature header') logging.verbose('Missing signature header')
@ -120,7 +124,7 @@ class ActorView(View):
try: try:
self.validate_signature(await self.request.read()) self.validate_signature(await self.request.read())
except aputils.SignatureFailureError as e: except SignatureFailureError as e:
logging.verbose('signature validation failed for "%s": %s', self.actor.id, e) logging.verbose('signature validation failed for "%s": %s', self.actor.id, e)
return Response.new_error(401, str(e), 'json') return Response.new_error(401, str(e), 'json')
@ -129,31 +133,31 @@ class ActorView(View):
headers = {key.lower(): value for key, value in self.request.headers.items()} headers = {key.lower(): value for key, value in self.request.headers.items()}
headers["(request-target)"] = " ".join([self.request.method.lower(), self.request.path]) headers["(request-target)"] = " ".join([self.request.method.lower(), self.request.path])
if (digest := aputils.Digest.new_from_digest(headers.get("digest"))): if (digest := Digest.new_from_digest(headers.get("digest"))):
if not body: if not body:
raise aputils.SignatureFailureError("Missing body for digest verification") raise SignatureFailureError("Missing body for digest verification")
if not digest.validate(body): if not digest.validate(body):
raise aputils.SignatureFailureError("Body digest does not match") raise SignatureFailureError("Body digest does not match")
if self.signature.algorithm_type == "hs2019": if self.signature.algorithm_type == "hs2019":
if "(created)" not in self.signature.headers: if "(created)" not in self.signature.headers:
raise aputils.SignatureFailureError("'(created)' header not used") raise SignatureFailureError("'(created)' header not used")
current_timestamp = aputils.HttpDate.new_utc().timestamp() current_timestamp = HttpDate.new_utc().timestamp()
if self.signature.created > current_timestamp: if self.signature.created > current_timestamp:
raise aputils.SignatureFailureError("Creation date after current date") raise SignatureFailureError("Creation date after current date")
if current_timestamp > self.signature.expires: if current_timestamp > self.signature.expires:
raise aputils.SignatureFailureError("Expiration date before current date") raise SignatureFailureError("Expiration date before current date")
headers["(created)"] = self.signature.created headers["(created)"] = self.signature.created
headers["(expires)"] = self.signature.expires headers["(expires)"] = self.signature.expires
# pylint: disable=protected-access # pylint: disable=protected-access
if not self.signer._validate_signature(headers, self.signature): if not self.signer._validate_signature(headers, self.signature):
raise aputils.SignatureFailureError("Signature does not match") raise SignatureFailureError("Signature does not match")
@register_route('/.well-known/webfinger') @register_route('/.well-known/webfinger')
@ -168,7 +172,7 @@ class WebfingerView(View):
if subject != f'acct:relay@{self.config.domain}': if subject != f'acct:relay@{self.config.domain}':
return Response.new_error(404, 'user not found', 'json') return Response.new_error(404, 'user not found', 'json')
data = aputils.Webfinger.new( data = Webfinger.new(
handle = 'relay', handle = 'relay',
domain = self.config.domain, domain = self.config.domain,
actor = self.config.actor actor = self.config.actor

View file

@ -1,9 +1,9 @@
from __future__ import annotations from __future__ import annotations
import aputils
import subprocess import subprocess
import typing import typing
from aputils.objects import Nodeinfo, WellKnownNodeinfo
from pathlib import Path from pathlib import Path
from .base import View, register_route from .base import View, register_route
@ -48,12 +48,12 @@ class NodeinfoView(View):
if niversion == '2.1': if niversion == '2.1':
data['repo'] = 'https://git.pleroma.social/pleroma/relay' data['repo'] = 'https://git.pleroma.social/pleroma/relay'
return Response.new(aputils.Nodeinfo.new(**data), ctype = 'json') return Response.new(Nodeinfo.new(**data), ctype = 'json')
@register_route('/.well-known/nodeinfo') @register_route('/.well-known/nodeinfo')
class WellknownNodeinfoView(View): class WellknownNodeinfoView(View):
async def get(self, request: Request) -> Response: async def get(self, request: Request) -> Response:
data = aputils.WellKnownNodeinfo.new_template(self.config.domain) data = WellKnownNodeinfo.new_template(self.config.domain)
return Response.new(data, ctype = 'json') return Response.new(data, ctype = 'json')

View file

@ -1,6 +1,6 @@
aiohttp>=3.9.1 aiohttp>=3.9.1
aiohttp-swagger[performance]==1.0.16 aiohttp-swagger[performance]==1.0.16
aputils@https://git.barkshark.xyz/barkshark/aputils/archive/0.1.7.tar.gz aputils@https://git.barkshark.xyz/barkshark/aputils/archive/0.1.6a.tar.gz
argon2-cffi==23.1.0 argon2-cffi==23.1.0
barkshark-sql@https://git.barkshark.xyz/barkshark/bsql/archive/0.1.1.tar.gz barkshark-sql@https://git.barkshark.xyz/barkshark/bsql/archive/0.1.1.tar.gz
click>=8.1.2 click>=8.1.2