mirror of
https://git.pleroma.social/pleroma/relay.git
synced 2024-11-23 23:17:58 +00:00
Compare commits
No commits in common. "ae490a9bf3e4f971ec4d4bc1c6506791ff246b4c" and "6f03e2ad4cffd5ca91359785801acce24b652ff7" have entirely different histories.
ae490a9bf3
...
6f03e2ad4c
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
__version__ = '0.3.0'
|
__version__ = '0.2.6'
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue