Compare commits

..

No commits in common. "6112734b2f62c86bd022ca5a49a740b6d4b0c62e" and "a6f1738b73b7715f21a25f03ec9a9b193559718e" have entirely different histories.

6 changed files with 35 additions and 36 deletions

View file

@ -98,8 +98,6 @@ class Connection(SqlConnection):
with self.run('put-config', params): with self.run('put-config', params):
pass pass
return data.get(key)
def get_inbox(self, value: str) -> Row: def get_inbox(self, value: str) -> Row:
with self.run('get-inbox', {'value': value}) as cur: with self.run('get-inbox', {'value': value}) as cur:
@ -194,7 +192,7 @@ class Connection(SqlConnection):
def put_user(self, username: str, password: str | None, handle: str | None = None) -> Row: def put_user(self, username: str, password: str | None, handle: str | None = None) -> Row:
if self.get_user(username): if self.get_user(username):
data: dict[str, str | datetime | None] = {} data: dict[str, str] = {}
if password: if password:
data['hash'] = self.hasher.hash(password) data['hash'] = self.hasher.hash(password)
@ -206,7 +204,7 @@ class Connection(SqlConnection):
stmt.set_where("username", username) stmt.set_where("username", username)
with self.query(stmt) as cur: with self.query(stmt) as cur:
return cur.one() # type: ignore return cur.one()
if password is None: if password is None:
raise ValueError('Password cannot be empty') raise ValueError('Password cannot be empty')

View file

@ -149,12 +149,7 @@ class HttpClient:
return None return None
async def get(self, async def get(self, url: str, sign_headers: bool, cls: type[T], force: bool = False) -> T | None:
url: str,
sign_headers: bool,
cls: type[T],
force: bool = False) -> T | None:
if not issubclass(cls, JsonBase): if not issubclass(cls, JsonBase):
raise TypeError('cls must be a sub-class of "aputils.JsonBase"') raise TypeError('cls must be a sub-class of "aputils.JsonBase"')
@ -179,12 +174,12 @@ class HttpClient:
headers.update(get_app().signer.sign_headers('POST', url, message, algorithm=algorithm)) headers.update(get_app().signer.sign_headers('POST', url, message, algorithm=algorithm))
try: try:
logging.verbose('Sending "%s" to %s', message.type.value, url) logging.verbose('Sending "%s" to %s', message.type, url)
async with self._session.post(url, headers = headers, data = message.to_json()) as resp: async with self._session.post(url, headers = headers, data = message.to_json()) as resp:
# Not expecting a response, so just return # Not expecting a response, so just return
if resp.status in {200, 202}: if resp.status in {200, 202}:
logging.verbose('Successfully sent "%s" to %s', message.type.value, url) logging.verbose('Successfully sent "%s" to %s', message.type, url)
return return
logging.verbose('Received error when pushing to %s: %i', url, resp.status) logging.verbose('Received error when pushing to %s: %i', url, resp.status)

View file

@ -63,10 +63,10 @@ def set_level(level: LogLevel | str) -> None:
def verbose(message: str, *args: Any, **kwargs: Any) -> None: def verbose(message: str, *args: Any, **kwargs: Any) -> None:
if not logging.root.isEnabledFor(LogLevel.VERBOSE): if not logging.root.isEnabledFor(LogLevel['VERBOSE']):
return return
logging.log(LogLevel.VERBOSE, message, *args, **kwargs) logging.log(LogLevel['VERBOSE'], message, *args, **kwargs)
debug: Callable = logging.debug debug: Callable = logging.debug
@ -76,6 +76,8 @@ error: Callable = logging.error
critical: Callable = logging.critical critical: Callable = logging.critical
env_log_level: Path | str | None = os.environ.get('LOG_LEVEL', 'INFO').upper()
try: try:
env_log_file: Path | None = Path(os.environ['LOG_FILE']).expanduser().resolve() env_log_file: Path | None = Path(os.environ['LOG_FILE']).expanduser().resolve()
@ -87,16 +89,10 @@ handlers: list[Any] = [logging.StreamHandler()]
if env_log_file: if env_log_file:
handlers.append(logging.FileHandler(env_log_file)) handlers.append(logging.FileHandler(env_log_file))
if os.environ.get('INVOCATION_ID'): logging.addLevelName(LogLevel['VERBOSE'], 'VERBOSE')
logging_format = '%(levelname)s: %(message)s'
else:
logging_format = '[%(asctime)s] %(levelname)s: %(message)s'
logging.addLevelName(LogLevel.VERBOSE, 'VERBOSE')
logging.basicConfig( logging.basicConfig(
level = LogLevel.INFO, level = LogLevel.INFO,
format = logging_format, format = '[%(asctime)s] %(levelname)s: %(message)s',
datefmt = '%Y-%m-%d %H:%M:%S', datefmt = '%Y-%m-%d %H:%M:%S',
handlers = handlers handlers = handlers
) )

View file

@ -130,8 +130,10 @@ class Message(aputils.Message):
description: str | None = None, description: str | None = None,
approves: bool = False) -> Self: approves: bool = False) -> Self:
return cls.new(aputils.ObjectType.APPLICATION, { return cls({
'@context': 'https://www.w3.org/ns/activitystreams',
'id': f'https://{host}/actor', 'id': f'https://{host}/actor',
'type': 'Application',
'preferredUsername': 'relay', 'preferredUsername': 'relay',
'name': 'ActivityRelay', 'name': 'ActivityRelay',
'summary': description or 'ActivityRelay bot', 'summary': description or 'ActivityRelay bot',
@ -153,8 +155,10 @@ class Message(aputils.Message):
@classmethod @classmethod
def new_announce(cls: type[Self], host: str, obj: str | dict[str, Any]) -> Self: def new_announce(cls: type[Self], host: str, obj: str | dict[str, Any]) -> Self:
return cls.new(aputils.ObjectType.ANNOUNCE, { return cls({
'@context': 'https://www.w3.org/ns/activitystreams',
'id': f'https://{host}/activities/{uuid4()}', 'id': f'https://{host}/activities/{uuid4()}',
'type': 'Announce',
'to': [f'https://{host}/followers'], 'to': [f'https://{host}/followers'],
'actor': f'https://{host}/actor', 'actor': f'https://{host}/actor',
'object': obj 'object': obj
@ -163,18 +167,22 @@ class Message(aputils.Message):
@classmethod @classmethod
def new_follow(cls: type[Self], host: str, actor: str) -> Self: def new_follow(cls: type[Self], host: str, actor: str) -> Self:
return cls.new(aputils.ObjectType.FOLLOW, { return cls({
'id': f'https://{host}/activities/{uuid4()}', '@context': 'https://www.w3.org/ns/activitystreams',
'type': 'Follow',
'to': [actor], 'to': [actor],
'object': actor, 'object': actor,
'id': f'https://{host}/activities/{uuid4()}',
'actor': f'https://{host}/actor' 'actor': f'https://{host}/actor'
}) })
@classmethod @classmethod
def new_unfollow(cls: type[Self], host: str, actor: str, follow: dict[str, str]) -> Self: def new_unfollow(cls: type[Self], host: str, actor: str, follow: dict[str, str]) -> Self:
return cls.new(aputils.ObjectType.UNDO, { return cls({
'@context': 'https://www.w3.org/ns/activitystreams',
'id': f'https://{host}/activities/{uuid4()}', 'id': f'https://{host}/activities/{uuid4()}',
'type': 'Undo',
'to': [actor], 'to': [actor],
'actor': f'https://{host}/actor', 'actor': f'https://{host}/actor',
'object': follow 'object': follow
@ -183,8 +191,10 @@ class Message(aputils.Message):
@classmethod @classmethod
def new_response(cls: type[Self], host: str, actor: str, followid: str, accept: bool) -> Self: def new_response(cls: type[Self], host: str, actor: str, followid: str, accept: bool) -> Self:
return cls.new(aputils.ObjectType.ACCEPT if accept else aputils.ObjectType.REJECT, { return cls({
'@context': 'https://www.w3.org/ns/activitystreams',
'id': f'https://{host}/activities/{uuid4()}', 'id': f'https://{host}/activities/{uuid4()}',
'type': 'Accept' if accept else 'Reject',
'to': [actor], 'to': [actor],
'actor': f'https://{host}/actor', 'actor': f'https://{host}/actor',
'object': { 'object': {

View file

@ -136,20 +136,20 @@ class ActorView(View):
if not digest.validate(body): if not digest.validate(body):
raise aputils.SignatureFailureError("Body digest does not match") raise aputils.SignatureFailureError("Body digest does not match")
if self.signature.algorithm_type == aputils.AlgorithmType.HS2019: if self.signature.algorithm_type == "hs2019":
if self.signature.created is None or self.signature.expires is None: if "(created)" not in self.signature.headers:
raise aputils.SignatureFailureError("Missing 'created' or 'expireds' parameter") raise aputils.SignatureFailureError("'(created)' header not used")
current_timestamp = aputils.HttpDate.new_utc().timestamp() current_timestamp = aputils.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 aputils.SignatureFailureError("Creation date after current date")
if self.signature.expires < current_timestamp: if current_timestamp > self.signature.expires:
raise aputils.SignatureFailureError("Signature has expired") raise aputils.SignatureFailureError("Expiration date before current date")
headers["(created)"] = str(self.signature.created) headers["(created)"] = self.signature.created
headers["(expires)"] = str(self.signature.expires) headers["(expires)"] = self.signature.expires
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 aputils.SignatureFailureError("Signature does not match")

View file

@ -1,6 +1,6 @@
activitypub-utils == 0.1.9
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
argon2-cffi == 23.1.0 argon2-cffi == 23.1.0
barkshark-sql @ https://git.barkshark.xyz/barkshark/bsql/archive/0.1.2.tar.gz barkshark-sql @ https://git.barkshark.xyz/barkshark/bsql/archive/0.1.2.tar.gz
click >= 8.1.2 click >= 8.1.2