diff --git a/relay/database/connection.py b/relay/database/connection.py index 4e575f6..8c64641 100644 --- a/relay/database/connection.py +++ b/relay/database/connection.py @@ -98,6 +98,8 @@ class Connection(SqlConnection): with self.run('put-config', params): pass + return data.get(key) + def get_inbox(self, value: str) -> Row: with self.run('get-inbox', {'value': value}) as cur: @@ -192,7 +194,7 @@ class Connection(SqlConnection): def put_user(self, username: str, password: str | None, handle: str | None = None) -> Row: if self.get_user(username): - data: dict[str, str] = {} + data: dict[str, str | datetime | None] = {} if password: data['hash'] = self.hasher.hash(password) @@ -204,7 +206,7 @@ class Connection(SqlConnection): stmt.set_where("username", username) with self.query(stmt) as cur: - return cur.one() + return cur.one() # type: ignore if password is None: raise ValueError('Password cannot be empty') diff --git a/relay/http_client.py b/relay/http_client.py index b51caf8..a475a5a 100644 --- a/relay/http_client.py +++ b/relay/http_client.py @@ -149,7 +149,12 @@ class HttpClient: return None - async def get(self, url: str, sign_headers: bool, cls: type[T], force: bool = False) -> T | None: + async def get(self, + url: str, + sign_headers: bool, + cls: type[T], + force: bool = False) -> T | None: + if not issubclass(cls, JsonBase): raise TypeError('cls must be a sub-class of "aputils.JsonBase"') @@ -174,12 +179,12 @@ class HttpClient: headers.update(get_app().signer.sign_headers('POST', url, message, algorithm=algorithm)) try: - logging.verbose('Sending "%s" to %s', message.type, url) + logging.verbose('Sending "%s" to %s', message.type.value, url) async with self._session.post(url, headers = headers, data = message.to_json()) as resp: # Not expecting a response, so just return if resp.status in {200, 202}: - logging.verbose('Successfully sent "%s" to %s', message.type, url) + logging.verbose('Successfully sent "%s" to %s', message.type.value, url) return logging.verbose('Received error when pushing to %s: %i', url, resp.status) diff --git a/relay/misc.py b/relay/misc.py index cb901bc..5af6e60 100644 --- a/relay/misc.py +++ b/relay/misc.py @@ -130,10 +130,8 @@ class Message(aputils.Message): description: str | None = None, approves: bool = False) -> Self: - return cls({ - '@context': 'https://www.w3.org/ns/activitystreams', + return cls.new(aputils.ObjectType.APPLICATION, { 'id': f'https://{host}/actor', - 'type': 'Application', 'preferredUsername': 'relay', 'name': 'ActivityRelay', 'summary': description or 'ActivityRelay bot', @@ -155,10 +153,8 @@ class Message(aputils.Message): @classmethod def new_announce(cls: type[Self], host: str, obj: str | dict[str, Any]) -> Self: - return cls({ - '@context': 'https://www.w3.org/ns/activitystreams', + return cls.new(aputils.ObjectType.ANNOUNCE, { 'id': f'https://{host}/activities/{uuid4()}', - 'type': 'Announce', 'to': [f'https://{host}/followers'], 'actor': f'https://{host}/actor', 'object': obj @@ -167,22 +163,18 @@ class Message(aputils.Message): @classmethod def new_follow(cls: type[Self], host: str, actor: str) -> Self: - return cls({ - '@context': 'https://www.w3.org/ns/activitystreams', - 'type': 'Follow', + return cls.new(aputils.ObjectType.FOLLOW, { + 'id': f'https://{host}/activities/{uuid4()}', 'to': [actor], 'object': actor, - 'id': f'https://{host}/activities/{uuid4()}', 'actor': f'https://{host}/actor' }) @classmethod def new_unfollow(cls: type[Self], host: str, actor: str, follow: dict[str, str]) -> Self: - return cls({ - '@context': 'https://www.w3.org/ns/activitystreams', + return cls.new(aputils.ObjectType.UNDO, { 'id': f'https://{host}/activities/{uuid4()}', - 'type': 'Undo', 'to': [actor], 'actor': f'https://{host}/actor', 'object': follow @@ -191,10 +183,8 @@ class Message(aputils.Message): @classmethod def new_response(cls: type[Self], host: str, actor: str, followid: str, accept: bool) -> Self: - return cls({ - '@context': 'https://www.w3.org/ns/activitystreams', + return cls.new(aputils.ObjectType.ACCEPT if accept else aputils.ObjectType.REJECT, { 'id': f'https://{host}/activities/{uuid4()}', - 'type': 'Accept' if accept else 'Reject', 'to': [actor], 'actor': f'https://{host}/actor', 'object': { diff --git a/relay/views/activitypub.py b/relay/views/activitypub.py index 68f1c23..9f8afb0 100644 --- a/relay/views/activitypub.py +++ b/relay/views/activitypub.py @@ -136,20 +136,20 @@ class ActorView(View): if not digest.validate(body): raise aputils.SignatureFailureError("Body digest does not match") - if self.signature.algorithm_type == "hs2019": - if "(created)" not in self.signature.headers: - raise aputils.SignatureFailureError("'(created)' header not used") + if self.signature.algorithm_type == aputils.AlgorithmType.HS2019: + if self.signature.created is None or self.signature.expires is None: + raise aputils.SignatureFailureError("Missing 'created' or 'expireds' parameter") current_timestamp = aputils.HttpDate.new_utc().timestamp() if self.signature.created > current_timestamp: raise aputils.SignatureFailureError("Creation date after current date") - if current_timestamp > self.signature.expires: - raise aputils.SignatureFailureError("Expiration date before current date") + if self.signature.expires < current_timestamp: + raise aputils.SignatureFailureError("Signature has expired") - headers["(created)"] = self.signature.created - headers["(expires)"] = self.signature.expires + headers["(created)"] = str(self.signature.created) + headers["(expires)"] = str(self.signature.expires) if not self.signer._validate_signature(headers, self.signature): raise aputils.SignatureFailureError("Signature does not match") diff --git a/requirements.txt b/requirements.txt index a0da5af..3b2d667 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ +activitypub-utils == 0.1.9 aiohttp >= 3.9.1 aiohttp-swagger[performance] == 1.0.16 -aputils @ https://git.barkshark.xyz/barkshark/aputils/archive/0.1.7.tar.gz argon2-cffi == 23.1.0 barkshark-sql @ https://git.barkshark.xyz/barkshark/bsql/archive/0.1.2.tar.gz click >= 8.1.2