import logging from urllib.parse import urlparse import requests from Crypto.PublicKey import RSA from .errors import ActivityNotFoundError from .urlutils import check_url logger = logging.getLogger(__name__) class NotAnActorError(Exception): def __init__(self, activity): self.activity = activity class ActorService(object): def __init__(self, user_agent, col, actor_id, actor_data, instances): logger.debug(f'Initializing ActorService user_agent={user_agent}') self._user_agent = user_agent self._col = col self._in_mem = {actor_id: actor_data} self._instances = instances self._known_instances = set() def _fetch(self, actor_url): logger.debug(f'fetching remote object {actor_url}') check_url(actor_url) resp = requests.get(actor_url, headers={ 'Accept': 'application/activity+json', 'User-Agent': self._user_agent, }) if resp.status_code == 404: raise ActivityNotFoundError(f'{actor_url} cannot be fetched, 404 not found error') resp.raise_for_status() return resp.json() def get(self, actor_url, reload_cache=False): logger.info(f'get actor {actor_url} (reload_cache={reload_cache})') if actor_url in self._in_mem: return self._in_mem[actor_url] instance = urlparse(actor_url)._replace(path='', query='', fragment='').geturl() if instance not in self._known_instances: self._known_instances.add(instance) if not self._instances.find_one({'instance': instance}): self._instances.insert({'instance': instance, 'first_object': actor_url}) if reload_cache: actor = self._fetch(actor_url) self._in_mem[actor_url] = actor self._col.update({'actor_id': actor_url}, {'$set': {'cached_response': actor}}, upsert=True) return actor cached_actor = self._col.find_one({'actor_id': actor_url}) if cached_actor: return cached_actor['cached_response'] actor = self._fetch(actor_url) if not 'type' in actor: raise NotAnActorError(None) if actor['type'] != 'Person': raise NotAnActorError(actor) self._col.update({'actor_id': actor_url}, {'$set': {'cached_response': actor}}, upsert=True) self._in_mem[actor_url] = actor return actor def get_public_key(self, actor_url, reload_cache=False): profile = self.get(actor_url, reload_cache=reload_cache) pub = profile['publicKey'] return pub['id'], RSA.importKey(pub['publicKeyPem']) def get_inbox_url(self, actor_url, reload_cache=False): profile = self.get(actor_url, reload_cache=reload_cache) return profile.get('inbox')