Support actor refresh while checking HTTP sig

main
Thomas Sileo 2022-10-07 12:05:28 +02:00
parent f6cfe06f66
commit 602da69083
1 changed files with 31 additions and 9 deletions

View File

@ -88,8 +88,12 @@ def _body_digest(body: bytes) -> str:
return "SHA-256=" + base64.b64encode(h.digest()).decode("utf-8") return "SHA-256=" + base64.b64encode(h.digest()).decode("utf-8")
async def _get_public_key(db_session: AsyncSession, key_id: str) -> Key: async def _get_public_key(
if cached_key := _KEY_CACHE.get(key_id): db_session: AsyncSession,
key_id: str,
should_skip_cache: bool = False,
) -> Key:
if not should_skip_cache and (cached_key := _KEY_CACHE.get(key_id)):
logger.info(f"Key {key_id} found in cache") logger.info(f"Key {key_id} found in cache")
return cached_key return cached_key
@ -101,12 +105,13 @@ async def _get_public_key(db_session: AsyncSession, key_id: str) -> Key:
select(models.Actor).where(models.Actor.ap_id == key_id.split("#")[0]) select(models.Actor).where(models.Actor.ap_id == key_id.split("#")[0])
) )
).one_or_none() ).one_or_none()
if existing_actor and existing_actor.public_key_id == key_id: if not should_skip_cache:
k = Key(existing_actor.ap_id, key_id) if existing_actor and existing_actor.public_key_id == key_id:
k.load_pub(existing_actor.public_key_as_pem) k = Key(existing_actor.ap_id, key_id)
logger.info(f"Found {key_id} on an existing actor") k.load_pub(existing_actor.public_key_as_pem)
_KEY_CACHE[key_id] = k logger.info(f"Found {key_id} on an existing actor")
return k _KEY_CACHE[key_id] = k
return k
# Fetch it # Fetch it
from app import activitypub as ap from app import activitypub as ap
@ -133,6 +138,13 @@ async def _get_public_key(db_session: AsyncSession, key_id: str) -> Key:
f"failed to fetch requested key {key_id}: got {actor['publicKey']}" f"failed to fetch requested key {key_id}: got {actor['publicKey']}"
) )
if should_skip_cache and actor["type"] != "Key" and existing_actor:
# We had to skip the cache, which means the actor key probably changed
# and we want to update our cached version
existing_actor.ap_actor = actor
existing_actor.updated_at = now()
await db_session.commit()
_KEY_CACHE[key_id] = k _KEY_CACHE[key_id] = k
return k return k
@ -216,7 +228,17 @@ async def httpsig_checker(
has_valid_signature = _verify_h( has_valid_signature = _verify_h(
signed_string, base64.b64decode(hsig["signature"]), k.pubkey signed_string, base64.b64decode(hsig["signature"]), k.pubkey
) )
# FIXME: fetch/update the user if the signature is wrong
# If the signature is not valid, we may have to update the cached actor
if not has_valid_signature:
logger.info("Invalid signature, trying to refresh actor")
try:
k = await _get_public_key(db_session, hsig["keyId"], should_skip_cache=True)
has_valid_signature = _verify_h(
signed_string, base64.b64decode(hsig["signature"]), k.pubkey
)
except Exception:
logger.exception("Failed to refresh actor")
httpsig_info = HTTPSigInfo( httpsig_info = HTTPSigInfo(
has_valid_signature=has_valid_signature, has_valid_signature=has_valid_signature,